Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Lines marked **Upgrade step** need you to act after updating.
- A form's pages have one top bar instead of two: the form's name with its menu, a Draft or Live label, the pages, Preview, Publish and your account. Publish first checks for questions without text, rules that point to a deleted question and a missing privacy link. Once the form is live, Share has its link, Open form and Unpublish. On a phone the pages sit in a tab bar at the bottom. (#265)
- The form editor shows your whole form as one column. Each question shows its number, answer type, text, and if it is required, has rules or is hidden. The question you work on opens right there, also on a phone, where you couldn't edit questions before. Hiding a question, random order and custom answers are now under the question's Settings. An outline next to the form jumps to any question, and on a phone a question's Settings open from the bottom of the screen. Move a question with Alt+Up/Down, with Move up and Move down in its menu, or by dragging. (#268, #272, #275)
- Deleting a question, a group, an answer choice, a consent policy or a logic rule in the form editor now asks first in the app's own dialog. It names the question, how many answers will be deleted with it, and the questions whose rules will stop working. A choice nobody picked, or a policy nobody agreed to, is still deleted at once. (#269)
- Each rule now shows as a sentence under its question, and a group says when it's hidden. Edit changes a rule right there, and for choices, ratings and scales you pick the answer instead of typing it. A rule whose question was deleted shows in red and can't be saved until you fix it. (#274)

### Fixed

Expand Down
1 change: 0 additions & 1 deletion resources/js/Pages/Forms/Edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const blocks = [
const stubs = {
AppLayout: { props: ["title"], template: "<main><slot /></main>" },
DocumentList: { template: "<ol><li>questions</li></ol>" },
BlockLogicEditor: true,
};

let wrapper: VueWrapper | undefined;
Expand Down
2 changes: 2 additions & 0 deletions resources/js/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
faTextSlash,
faTrash,
faTree,
faTriangleExclamation,
faTrophy,
faUnderline,
faUser,
Expand Down Expand Up @@ -129,6 +130,7 @@ library.add(
faTextSlash,
faTrash,
faTree,
faTriangleExclamation,
faTrophy,
faUnderline,
faUser,
Expand Down
16 changes: 9 additions & 7 deletions resources/js/components/Factory/Document/DocumentGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,23 @@
title="Use a group to show or hide several questions with one rule."
class="h-11 min-w-0 flex-1 rounded-md border-0 bg-transparent px-2 text-base font-semibold lg:h-9 lg:text-sm text-grey-700 placeholder:text-grey-600 hover:bg-grey-100 focus:bg-white focus:ring-2 focus:ring-blue-500"
/>
<span v-if="group.is_disabled" class="text-xs text-grey-600">Hidden</span>
<span v-if="group.is_disabled" class="text-xs text-grey-600"
>Always hidden</span
>
<button
type="button"
class="flex h-11 items-center rounded-md px-2 text-sm font-semibold text-blue-700 lg:h-9 hover:bg-blue-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
@click="logic.showLogicEditor(group)"
@click="addRule(group)"
>
Rules<span v-if="group.logics?.length" class="tabular-nums"
>&nbsp;({{ group.logics.length }})</span
>
Add rule
</button>
<BlockMenu
:block="group"
:label="`Options for group ${group.title || 'Group'}`"
@add-to-group="addQuestion"
/>
</div>
<BlockRules :block="group" :numbers="numbers" class="pb-2 pl-8" />

<div class="relative">
<DocumentList
Expand Down Expand Up @@ -73,9 +74,11 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { D9Icon } from "@deck9/ui";
import { useForm, useLogic, useWorkbench } from "@/stores";
import { useForm, useWorkbench } from "@/stores";
import BlockMenu from "./BlockMenu.vue";
import DocumentList from "./DocumentList.vue";
import BlockRules from "./Rules/BlockRules.vue";
import { addRule } from "./Rules/ruleEditing";
import { useAddBlock } from "./blockFocus";
import { useMoveBlock } from "./blockMove";

Expand All @@ -86,7 +89,6 @@ const props = defineProps<{

const store = useForm();
const workbench = useWorkbench();
const logic = useLogic();
const { addBlock } = useAddBlock();
const { onMoveKey } = useMoveBlock();

Expand Down
7 changes: 7 additions & 0 deletions resources/js/components/Factory/Document/DocumentList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
:numbers="numbers"
/>
<DocumentQuestion v-else :block="block" :number="numbers[block.uuid]" />
<BlockRules
v-if="block.type !== 'group'"
:block="block"
:numbers="numbers"
class="pl-8 sm:pl-12"
/>
</div>
</Draggable>
</Container>
Expand All @@ -33,6 +39,7 @@ import { Container, Draggable } from "vue3-smooth-dnd";
import { useForm } from "@/stores";
import DocumentGroup from "./DocumentGroup.vue";
import DocumentQuestion from "./DocumentQuestion.vue";
import BlockRules from "./Rules/BlockRules.vue";

const props = defineProps<{
groupId?: string;
Expand Down
23 changes: 21 additions & 2 deletions resources/js/components/Factory/Document/DocumentQuestion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ describe("a closed question in the document", () => {
expect(text).toContain("Which days can you help?");
expect(text).toContain("Checkboxes · 2 choices");
expect(text).toContain("Required");
expect(text).toContain("1 rule");
expect(text).toContain("Hidden");
// its rules show as sentences under it
expect(text).not.toContain("rule");
expect(text).toContain("Always hidden");
expect(text).toContain("days");
expect(text).not.toContain(question.uuid);
});
Expand All @@ -66,6 +67,24 @@ describe("a closed question in the document", () => {
expect(text).not.toContain(question.uuid);
});

it("marks its number while the open question jumps here", async () => {
const workbench = useWorkbench();
const wrapper = mountQuestion();
const number = () => wrapper.find("span[aria-hidden] > span");

expect(number().text()).toBe("3");
expect(number().classes()).not.toContain("bg-blue-50");

workbench.block = {
id: 1,
uuid: "q-1",
logics: [{ action: "goto", action_payload: question.uuid }],
} as unknown as FormBlockModel;
await wrapper.vm.$nextTick();

expect(number().classes()).toContain("bg-blue-50");
});

it("opens as a card when clicked", async () => {
const workbench = useWorkbench();
const wrapper = mountQuestion();
Expand Down
19 changes: 13 additions & 6 deletions resources/js/components/Factory/Document/DocumentQuestion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
<span
aria-hidden="true"
class="flex h-6 items-center text-[13px] tabular-nums text-grey-600"
>{{ number }}</span
><span
:class="
isJumpTarget &&
'relative z-10 flex h-5 items-center rounded-full border border-blue-200 bg-blue-50 px-1.5 text-xs font-semibold text-blue-700'
"
>{{ number }}</span
></span
>
<span
aria-hidden="true"
Expand Down Expand Up @@ -53,10 +59,7 @@
</template></span
>
<span v-if="block.is_required" class="text-red-700">Required</span>
<span v-if="ruleCount" class="tabular-nums"
>{{ ruleCount }} {{ ruleCount === 1 ? "rule" : "rules" }}</span
>
<span v-if="block.is_disabled">Hidden</span>
<span v-if="block.is_disabled">Always hidden</span>
<span v-if="block.title" class="break-all font-mono">{{
block.title
}}</span>
Expand All @@ -74,6 +77,7 @@ import { plainText } from "./questionSummary";
import QuestionCard from "./QuestionCard.vue";
import { cursorFor } from "./blockFocus";
import { useMoveBlock } from "./blockMove";
import { jumpTargets } from "./Rules/ruleText";

const props = defineProps<{
block: FormBlockModel;
Expand All @@ -84,6 +88,10 @@ const workbench = useWorkbench();
const { types } = useBlockTypes();

const isOpen = computed(() => workbench.block?.id === props.block.id);
// the open question's rules jump here
const isJumpTarget = computed(() =>
jumpTargets(workbench.block).includes(props.block.uuid),
);

const text = computed(() => plainText(props.block.message));
const blockType = computed(() =>
Expand All @@ -96,7 +104,6 @@ const choiceCount = computed(() =>
).length
: null,
);
const ruleCount = computed(() => props.block.logics?.length ?? 0);

const { onMoveKey } = useMoveBlock();
const row = ref<HTMLButtonElement | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const stubs = {
DocumentList: {
template: `<div><div id="row-b1" /><div id="row-b2" /></div>`,
},
BlockLogicEditor: true,
RuleGutter: true,
};

let wrapper: VueWrapper | undefined;
Expand Down
4 changes: 2 additions & 2 deletions resources/js/components/Factory/Document/FormDocument.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</button>
</div>

<RuleGutter />
<DocumentList v-if="store.hasBlocks" :numbers="numbers" />
<EmptyState
v-else
Expand Down Expand Up @@ -94,7 +95,6 @@
</DocumentOutline>
</DialogPanel>
</Dialog>
<BlockLogicEditor />
<p class="sr-only" aria-live="polite">{{ moveNote }}</p>
</template>

Expand All @@ -105,9 +105,9 @@ import { D9Button, D9Icon } from "@deck9/ui";
import { useForm, useWorkbench } from "@/stores";
import EmptyState from "@/components/EmptyState.vue";
import QuestionsLoadState from "@/components/Factory/Shared/QuestionsLoadState.vue";
import BlockLogicEditor from "@/components/Factory/Sidebar/BlockLogicEditor.vue";
import DocumentList from "./DocumentList.vue";
import DocumentOutline from "./DocumentOutline.vue";
import RuleGutter from "./Rules/RuleGutter.vue";
import { questionNumbers } from "./questionSummary";
import { moveNote } from "./blockMove";
import {
Expand Down
10 changes: 4 additions & 6 deletions resources/js/components/Factory/Document/QuestionCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@
<button
type="button"
class="flex h-11 items-center gap-1 rounded-md px-2 text-sm font-semibold text-blue-700 lg:h-9 hover:bg-blue-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
@click="logic.showLogicEditor(block)"
@click="addRule(block)"
>
Rules<span v-if="block.logics?.length" class="tabular-nums"
>&nbsp;({{ block.logics.length }})</span
>
Add rule
</button>
<BlockMenu :block="block" :label="`Options for question ${number}`" />
</div>
Expand Down Expand Up @@ -123,14 +121,15 @@ import { computed, nextTick, onMounted, provide, ref, watch } from "vue";
import { Dialog, DialogPanel, DialogTitle } from "@headlessui/vue";
import { useMediaQuery } from "@vueuse/core";
import { D9Icon, D9Select, D9Switch } from "@deck9/ui";
import { useLogic, useWorkbench } from "@/stores";
import { useWorkbench } from "@/stores";
import Editor from "@/components/Factory/Shared/Editor.vue";
import ConfigureClicks from "@/components/Factory/Main/ConfigureClicks.vue";
import ConfigureConsents from "@/components/Factory/Main/ConfigureConsents.vue";
import { useBlockTypes } from "@/components/Factory/utils/useBlockTypes";
import BlockMenu from "./BlockMenu.vue";
import QuestionSettings from "./QuestionSettings.vue";
import { cursorFor } from "./blockFocus";
import { addRule } from "./Rules/ruleEditing";

const props = defineProps<{
block: FormBlockModel;
Expand All @@ -141,7 +140,6 @@ const props = defineProps<{
provide("block", props.block);

const workbench = useWorkbench();
const logic = useLogic();
const { types } = useBlockTypes();

const id = computed(() => `question-${props.block.uuid}`);
Expand Down
Loading