Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tailwind-components): Boolean field input #4630

Merged
merged 13 commits into from
Jan 29, 2025
2 changes: 1 addition & 1 deletion apps/metadata-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export interface IFormLegendSection {
}

export type columnId = string;
export type columnValue = string | number | boolean | columnValueObject;
export type columnValue = string | number | boolean | null | columnValueObject;

interface columnValueObject {
[x: string]: columnValue;
Expand Down
11 changes: 11 additions & 0 deletions apps/tailwind-components/components/form/FieldInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,16 @@ function validate(value: columnValue) {
@update:modelValue="$emit('update:modelValue', $event)"
@error="$emit('error', $event)"
/>
<LazyInputBoolean
v-else-if="type === 'BOOL'"
ref="input"
:id="id"
:label="label"
:required="required"
:modelValue="data === true || data === false ? data : null"
@focus="$emit('focus')"
@update:modelValue="$emit('update:modelValue', $event)"
@error="$emit('error', $event)"
/>
<LazyInputPlaceHolder v-else ref="input" :type="type" />
</template>
84 changes: 84 additions & 0 deletions apps/tailwind-components/components/input/Boolean.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<InputRadioGroup
:id="id"
:modelValue="props.modelValue"
:radioOptions="radioOptions"
:showClearButton="true"
align="horizontal"
connoratrug marked this conversation as resolved.
Show resolved Hide resolved
@update:modelValue="onInput"
@focus="$emit('focus')"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This is a fix for another PR)

When the radio group is focused, the inputs aren't visually focused. Focused styles for the input radio button is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what changes would you like to see ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like an issue, on the master branch related to the radio input

@blur="$emit('blur')"
/>
</template>

<script lang="ts" setup>
import type { IRadioOptionsData } from "~/types/types";

const props = withDefaults(
defineProps<{
id: string;
label?: string;
modelValue: boolean | null;
placeholder?: string;
disabled?: boolean;
required?: boolean;
valid?: boolean;
hasError?: boolean;
trueLabel?: string;
falseLabel?: string;
}>(),
{
disabled: false,
required: false,
hasError: false,
valid: false,
trueLabel: "True",
falseLabel: "False",
}
);

const emit = defineEmits([
"focus",
"blur",
"error",
"update:modelValue",
"input",
]);
defineExpose({ validate });

function validate(value: boolean | null) {
if (props.required && value === null) {
emit("error", [
{ message: `${props.label || props.id} required to complete the form` },
]);
} else {
emit("error", []);
}
}

const radioOptions = ref<IRadioOptionsData[]>([
{ value: true, label: props.trueLabel },
{ value: false, label: props.falseLabel },
]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not required then a 'clear' is null is possible?


const modelValue = ref<boolean | undefined>(undefined);

function toBoolean(value: string) {
switch (value) {
case "true":
return true;

case "false":
return false;

default:
return null;
}
}

function onInput(value: string) {
const booleanValue = toBoolean(value);
emit("update:modelValue", booleanValue);
validate(booleanValue);
}
</script>
6 changes: 4 additions & 2 deletions apps/tailwind-components/components/input/Radio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
</template>

<script lang="ts" setup>
import type { columnValue } from "../../../metadata-utils/src/types";

defineProps<{
value?: string | boolean;
value?: columnValue;
}>();

const modelValue = defineModel<string | boolean>();
const modelValue = defineModel<columnValue>();
</script>
68 changes: 43 additions & 25 deletions apps/tailwind-components/components/input/RadioGroup.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<template>
<div :id="`${id}-radio-group`">
<div
:id="`${id}-radio-group`"
class="flex gap-1"
:class="{
'flex-row': align === 'horizontal',
'flex-col': align === 'vertical',
}"
>
<div v-for="option in radioOptions" class="flex justify-start align-center">
<InputRadio
:id="`${id}-radio-group-${option.value}`"
class="sr-only"
class="sr-only fixed"
connoratrug marked this conversation as resolved.
Show resolved Hide resolved
:name="id"
:value="option.value"
v-model="modelValue"
:checked="option.value === modelValue"
:modelValue="props.modelValue"
@input="$emit('update:modelValue', $event.target.value)"
connoratrug marked this conversation as resolved.
Show resolved Hide resolved
:checked="option.value === props.modelValue"
/>
<InputLabel
:for="`${id}-radio-group-${option.value}`"
class="hover:cursor-pointer flex flex-row gap-1"
class="hover:cursor-pointer flex flex-row gap-1 text-title"
>
<InputRadioIcon :checked="modelValue === option.value" class="mr-2.5" />
<InputRadioIcon :checked="modelValue === option.value" class="mr-1" />
<template v-if="option.label">
{{ option.label }}
</template>
Expand All @@ -22,40 +30,50 @@
</template>
</InputLabel>
</div>
<div class="mt-2" v-if="showClearButton">
<button
type="reset"
:id="`${id}-radio-group-clear`"
:form="`${id}-radio-group`"
@click.prevent="resetModelValue"
>
Clear
</button>
</div>

<button
v-show="isClearBtnShow"
class="ml-2 w-8 text-center text-button-outline hover:text-button-outline hover:underline"
:class="{ 'ml-2': align === 'horizontal', 'mt-2': align === 'vertical' }"
type="reset"
:id="`${id}-radio-group-clear`"
:form="`${id}-radio-group`"
@click.prevent="resetModelValue"
>
Clear
</button>
</div>
</template>

<script lang="ts" setup>
interface RadioOptionsDataIF {
value: string | boolean;
label?: string;
checked?: boolean | undefined;
}
import type { IRadioOptionsData } from "~/types/types";

withDefaults(
const props = withDefaults(
defineProps<{
id: string;
radioOptions: RadioOptionsDataIF[];
modelValue: boolean | string | null;
radioOptions: IRadioOptionsData[];
showClearButton?: boolean;
align?: "horizontal" | "vertical";
connoratrug marked this conversation as resolved.
Show resolved Hide resolved
}>(),
{
showClearButton: false,
align: "vertical",
}
);

const modelValue = defineModel<string | boolean>();
const emit = defineEmits(["update:modelValue"]);

function resetModelValue() {
modelValue.value = undefined;
emit("update:modelValue", null);
}

const isClearBtnShow = computed(() => {
return (
props.showClearButton &&
(props.modelValue === true ||
props.modelValue === false ||
(typeof props.modelValue === "string" && props.modelValue.length > 0))
);
});
</script>
12 changes: 7 additions & 5 deletions apps/tailwind-components/pages/Form.story.vue
Original file line number Diff line number Diff line change
Expand Up @@ -172,19 +172,21 @@ watch(
<div class="mt-4 flex flex-row">
<div v-if="Object.keys(formValues).length" class="basis-1/2">
<h3 class="text-label">Values</h3>
<dl class="flex">
<dl class="flex flex-col">
<template v-for="(value, key) in formValues">
<dt v-if="value" class="font-bold">{{ key }}:</dt>
<dd v-if="value" class="ml-1">{{ value }}</dd>
<dt class="font-bold">{{ key }}:</dt>
<dd v-if="value !== null && value !== undefined" class="pl-3">
{{ value }}
</dd>
</template>
</dl>
</div>
<div v-if="Object.keys(errors).length" class="basis-1/2">
<h3 class="text-label">Errors</h3>

<dl class="flex">
<dl class="flex flex-col">
<template v-for="(value, key) in errors">
<dt v-if="value.length" class="font-bold">{{ key }}:</dt>
<dt class="font-bold">{{ key }}:</dt>
<dd v-if="value.length" class="ml-1">{{ value }}</dd>
</template>
</dl>
Expand Down
25 changes: 25 additions & 0 deletions apps/tailwind-components/pages/input/Boolean.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<InputBoolean id="boolean-story-id" v-model="modelValue1" />
<div class="my-4">intit empty: {{ modelValue1 }}</div>

<InputBoolean id="boolean-story-id2" v-model="modelValue2" />
<div class="my-4">init true: {{ modelValue2 }}</div>

<InputBoolean id="boolean-story-id3" v-model="modelValue3" />
<div class="my-4">init false: {{ modelValue3 }}</div>

<InputBoolean
id="boolean-story-id4"
v-model="politeBoolean"
true-label="Yes please !"
false-label="No thank you"
/>
<div class="my-4">Be polite: {{ politeBoolean }}</div>
</template>

<script lang="ts" setup>
const modelValue1 = ref<null>(null);
const modelValue2 = ref<boolean>(true);
const modelValue3 = ref<boolean>(false);
const politeBoolean = ref<boolean>(false);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ test("it should render the form", async ({ page }) => {
await expect(page.getByRole("main")).toContainText("name");
await expect(page.getByRole("main")).toContainText("the name");
await expect(page.getByRole("main")).toContainText("date");
await expect(page.getByRole("main")).toContainText("Required");
await expect(page.getByLabel("name")).toBeVisible();
});

test("it should handle input", async ({ page }) => {
await page.getByLabel("name").pressSequentially("test");
await expect(page.getByLabel("name")).toHaveValue("test");
await page.getByRole("heading", { name: "Values" }).click();
await expect(page.getByRole("definition")).toContainText("test");
});

test("it should show the chapters in the legend", async ({ page }) => {
Expand Down
7 changes: 7 additions & 0 deletions apps/tailwind-components/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { IColumn } from "../../metadata-utils/dist";
import type { columnValue } from "../../metadata-utils/src/types";

export type Resp<T> = {
data: Record<string, T[]>;
Expand Down Expand Up @@ -91,4 +92,10 @@ export interface IDocumentation {
file: IFile;
}

export interface IRadioOptionsData {
value: columnValue;
label?: string;
checked?: boolean | undefined;
}