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

I9857 slider #343

Merged
merged 3 commits into from
Apr 10, 2024
Merged
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
5 changes: 5 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import {withThemeByDataAttribute} from '@storybook/addon-themes';
import {mockDateDecorator} from 'storybook-mock-date-decorator';
import PrimeVue from 'primevue/config';

import {setup} from '@storybook/vue3';
import GlobalMixins from '@/mixins/global.js';
Expand Down Expand Up @@ -51,6 +52,10 @@ initialize({

setup((app) => {
app.use(pinia);
app.use(PrimeVue, {
unstyled: true,
});

app.mixin(GlobalMixins);

app.use(FloatingVue, {
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"moment": "^2.29.4",
"ofetch": "^1.3.3",
"pinia": "^2.1.7",
"primevue": "^3.50.0",
"tiny-emitter": "^2.1.0",
"tinymce": "^5.10.7",
"uuid": "^9.0.0",
Expand Down
1 change: 1 addition & 0 deletions src/components/Form/FormFieldLabel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
export default {
name: 'FormFieldLabel',
props: {
labelId: String,
controlId: String,
label: String,
localeLabel: String,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Form/FormGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import FieldShowEnsuringLink from './fields/FieldShowEnsuringLink.vue';
import FieldText from './fields/FieldText.vue';
import FieldTextarea from './fields/FieldTextarea.vue';
import FieldUpload from './fields/FieldUpload.vue';
import FieldSlider from './fields/FieldSlider.vue';
import FieldUploadImage from './fields/FieldUploadImage.vue';

export default {
Expand All @@ -101,6 +102,7 @@ export default {
FieldShowEnsuringLink,
FieldText,
FieldTextarea,
FieldSlider,
FieldUpload,
FieldUploadImage,
},
Expand Down
11 changes: 10 additions & 1 deletion src/components/Form/fields/FieldBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ export default {
return this.isMultilingual ? this.name + '-' + this.localeKey : this.name;
},

/**
* In case field is not using input element, its necessary to reference the label via aria-labelby (for example FieldSlider)
*
* @return {String}
*/
labelId() {
return this.compileId('control');
},

/**
* A unique id for the label and control
*
Expand Down Expand Up @@ -185,7 +194,7 @@ export default {
if (this.isMultilingual) {
ids.push(this.multilingualProgressId);
}
return ids.length ? ids.join(' ') : false;
return ids.length ? ids.join(' ') : undefined;
},

/**
Expand Down
7 changes: 7 additions & 0 deletions src/components/Form/fields/FieldSlider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks';

import * as FieldSliderStories from './FieldSlider.stories.js';

<Meta of={FieldSliderStories} />

# FieldSlider
34 changes: 34 additions & 0 deletions src/components/Form/fields/FieldSlider.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import FieldSlider from './FieldSlider.vue';

import FieldBaseMock from '../mocks/field-base';
import FieldSliderMock from '../mocks/field-slider';

export default {
title: 'Forms/FieldSlider',
component: FieldSlider,
render: (args) => ({
components: {FieldSlider},
setup() {
function change(name, prop, newValue, localeKey) {
if (localeKey) {
args[prop][localeKey] = newValue;
} else {
args[prop] = newValue;
}
}

return {args, change};
},
template: `
<FieldSlider v-bind="args" @change="change" />
`,
}),
};

export const Base = {
args: {
...FieldBaseMock,
...FieldSliderMock,
// description: 'slider description',
},
};
184 changes: 184 additions & 0 deletions src/components/Form/fields/FieldSlider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<template>
<div class="mt-5 max-w-lg">
<div class="pkpFormField__heading">
<form-field-label
:id="labelId"
:label="label"
:locale-label="localeLabel"
:is-required="isRequired"
:required-label="t('common.required')"
:multilingual-label="multilingualLabel"
/>
<tooltip
v-if="isPrimaryLocale && tooltip"
aria-hidden="true"
:tooltip="tooltip"
label=""
/>
<span
v-if="isPrimaryLocale && tooltip"
:id="describedByTooltipId"
class="-screenReader"
v-html="tooltip"
/>
<help-button
v-if="isPrimaryLocale && helpTopic"
:id="describedByHelpId"
:topic="helpTopic"
:section="helpSection"
:label="t('help.help')"
/>
</div>
<div
v-if="isPrimaryLocale && description"
:id="describedByDescriptionId"
class="pkpFormField__description"
v-html="description"
/>
<div class="mt-2 flex">
<div class="mt-3 grow">
<div class="px-2">
<Slider
v-model="currentValue"
:min="min"
:max="max"
:step="step"
class="w-full"
:aria-labelledby="labelId"
:pt="sliderStyling"
/>
</div>
<div class="mt-2 flex justify-between text-base-normal text-secondary">
<div>{{ minLabel || min }}</div>
<div>{{ maxLabel || max }}</div>
</div>
</div>
<div
class="ms-3 w-48 self-start rounded border border-form-fields p-2 text-center text-base-normal text-secondary"
>
{{ displayedValue }}
</div>
</div>
</div>
</template>

<script>
import FieldBase from './FieldBase.vue';
import Slider from 'primevue/slider';

export default {
name: 'FieldSlider',
components: {Slider},
extends: FieldBase,
props: {
min: {required: true, type: Number},
max: {required: true, type: Number},
step: {required: false, type: Number, default: 1},
minLabel: {required: false, type: String, default: null},
maxLabel: {required: false, type: String, default: null},
/** Expecting translation key, which gets the value passed to it as {$value} */
valueLabel: {required: false, type: String, default: null},
valueLabelMin: {required: false, type: String, default: null},
valueLabelMax: {required: false, type: String, default: null},
},
data() {
const sliderStyling = {
root: ({props}) => ({
class: [
'relative',
// Size
{
'h-1 w-60': props.orientation == 'horizontal',
},
// Shape
'border-0',
// Colors
'bg-[#BBBBBB]',
// States
{
// Disabled use cases is not used, styling to be refine when needed

'select-none pointer-events-none cursor-default': props.disabled,
},
],
}),
range: ({props}) => ({
class: [
// Position
'block absolute',
{
'top-0 left-0': props.orientation == 'horizontal',
},
//Size
{
'h-full': props.orientation == 'horizontal',
},
// Colors
'bg-primary',
],
}),
handle: ({props, options}) => {
return {
id: this.controlId,
'aria-valuetext': this.displayedValue,
'aria-describedby': this.describedByIds,

class: [
'block',
// Size
'h-[1.143rem]',
'w-[1.143rem]',
{
'top-[50%] mt-[-0.5715rem] ml-[-0.5715rem]':
props.orientation == 'horizontal',
'left-[50%] mb-[-0.5715rem] ml-[-0.5715rem]':
props.orientation == 'vertical',
},
// Shape
'rounded-full',
'border-2',
// Colors
'bg-primary',
'border-primary',

// States
'hover:bg-hover hover:border-primary-500',
'focus-visible:outline-none focus-visible:outline-offset-0 focus-visible:ring',
'ring-primary/50',
// Transitions
'transition duration-200',
// Misc
'cursor-grab',
'touch-action-none',
],
};
},
// Additional styling is available if we need to have two handlers for range capabilities
};

return {sliderStyling};
},
computed: {
displayedValue() {
if (this.valueLabelMin && this.currentValue == this.min) {
return this.replaceLocaleParams(this.valueLabelMin, {
value: this.currentValue,
});
}
if (this.valueLabelMax && this.currentValue == this.max) {
return this.replaceLocaleParams(this.valueLabelMax, {
value: this.currentValue,
});
}

if (this.valueLabel) {
return this.replaceLocaleParams(this.valueLabel, {
value: this.currentValue,
});
}

return this.currentValue;
},
},
};
</script>
11 changes: 11 additions & 0 deletions src/components/Form/mocks/field-slider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
name: 'slider',
component: 'field-slider',
label: 'Review Request Response - Before Due Date',
value: 20,
min: 0,
minLabel: 'None',
max: 35,
valueLabel: '{$value} days after due date',
valueLabelMin: 'No reminder set',
};
8 changes: 8 additions & 0 deletions src/composables/useFiltersForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ export function useFiltersForm(_filtersForm) {
label: option.label,
value: option.value,
});
} else if (field.component === 'field-slider') {
if (fieldValue !== field.min) {
list.push({
fieldLabel: field.label,
value: fieldValue,
label: fieldValue,
});
}
} else {
list.push({
fieldLabel: field.label,
Expand Down
19 changes: 16 additions & 3 deletions src/composables/useForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ function getField(form, name) {
return fields.find((field) => field.name === name);
}

function getClearValue(field, localeKey = null) {
if (localeKey) {
if (field.component === 'field-slider') {
return field.min;
}
return Array.isArray(field.value[localeKey]) || field.selected ? [] : '';
}

if (field.component === 'field-slider') {
return field.min;
}
return Array.isArray(field.value) || field.selected ? [] : '';
}

function mapFromSelectedToValue(selected) {
return selected.map((iv) => iv.value);
}
Expand Down Expand Up @@ -43,13 +57,12 @@ export function useForm(_form) {
const newValueMultilingual = {};
form.value.supportedFormLocales.forEach((localeObject) => {
const localeKey = localeObject.key;
const newValue =
Array.isArray(field.value[localeKey]) || field.selected ? [] : '';
const newValue = getClearValue(field, localeKey);
newValueMultilingual[localeKey] = newValue;
});
setValue(field.name, newValueMultilingual);
} else {
const newValue = Array.isArray(field.value) || field.selected ? [] : '';
const newValue = getClearValue(field);
setValue(field.name, newValue);
}
});
Expand Down
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default {
},
borderRadius: {
DEFAULT: '4px',
full: '9999px',
},
boxShadow: {
DEFAULT: '0 0 4px rgba(0, 0, 0, 0.5);',
Expand Down