From c6be014479a3eb24c72d2e99e29a17c0f05b0099 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Tue, 31 Oct 2023 11:42:18 +0100 Subject: [PATCH 1/5] :arrow_up: [#454] Bump to design tokens 0.45.0 This release contains the (correct) design token values for the edit grid --- design-tokens | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/design-tokens b/design-tokens index 1fc3a5667..429fa1476 160000 --- a/design-tokens +++ b/design-tokens @@ -1 +1 @@ -Subproject commit 1fc3a5667ed656c554624aefaaca3650fd0bf94a +Subproject commit 429fa14766b18a558bd17e69ddf9aef26398af81 diff --git a/package.json b/package.json index 264d89e64..09638c32b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@floating-ui/react": "^0.24.2", "@formio/protected-eval": "^1.2.1", "@fortawesome/fontawesome-free": "^6.1.1", - "@open-formulieren/design-tokens": "^0.44.1", + "@open-formulieren/design-tokens": "^0.45.0", "@sentry/react": "^6.13.2", "@sentry/tracing": "^6.13.2", "@trivago/prettier-plugin-sort-imports": "^4.0.0", From 1474ace46b5c2e1e52f73c0edc03e0873e11932f Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Tue, 31 Oct 2023 11:43:32 +0100 Subject: [PATCH 2/5] :sparkles: [#454] Implement edit grid react component and styles This sets up the edit grid as a proper themable component using design tokens. --- src/components/EditGrid/EditGrid.js | 39 +++++++++ src/components/EditGrid/EditGrid.stories.js | 56 +++++++++++++ .../EditGrid/EditGridButtonGroup.js | 13 +++ src/components/EditGrid/EditGridItem.js | 21 +++++ .../EditGrid/EditGridItem.stories.js | 30 +++++++ src/components/EditGrid/design-tokens.mdx | 73 +++++++++++++++++ src/components/EditGrid/index.js | 3 + src/scss/components/_editgrid.scss | 82 +++++++++++-------- 8 files changed, 282 insertions(+), 35 deletions(-) create mode 100644 src/components/EditGrid/EditGrid.js create mode 100644 src/components/EditGrid/EditGrid.stories.js create mode 100644 src/components/EditGrid/EditGridButtonGroup.js create mode 100644 src/components/EditGrid/EditGridItem.js create mode 100644 src/components/EditGrid/EditGridItem.stories.js create mode 100644 src/components/EditGrid/design-tokens.mdx create mode 100644 src/components/EditGrid/index.js diff --git a/src/components/EditGrid/EditGrid.js b/src/components/EditGrid/EditGrid.js new file mode 100644 index 000000000..48d5a9b11 --- /dev/null +++ b/src/components/EditGrid/EditGrid.js @@ -0,0 +1,39 @@ +import {ButtonGroup} from '@utrecht/component-library-react'; +import PropTypes from 'prop-types'; +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +import {OFButton} from 'components/Button'; +import FAIcon from 'components/FAIcon'; + +const DefaultAddButtonLabel = () => ( + <> + {' '} + + +); + +const EditGrid = ({children, onAddItem, addButtonLabel}) => ( +
+
{children}
+ + {onAddItem && ( + + + {addButtonLabel || } + + + )} +
+); + +EditGrid.propTypes = { + children: PropTypes.node.isRequired, + addButtonLabel: PropTypes.node, + onAddItem: PropTypes.func, +}; + +export default EditGrid; diff --git a/src/components/EditGrid/EditGrid.stories.js b/src/components/EditGrid/EditGrid.stories.js new file mode 100644 index 000000000..e4c5bc035 --- /dev/null +++ b/src/components/EditGrid/EditGrid.stories.js @@ -0,0 +1,56 @@ +import Body from 'components/Body'; +import {OFButton} from 'components/Button'; + +import {EditGrid, EditGridButtonGroup, EditGridItem} from '.'; + +export default { + title: 'Pure React components / EditGrid / EditGrid', + component: EditGrid, + argTypes: { + children: {control: false}, + onAddItem: {control: false}, + }, + args: { + children: ( + <> + + A button + + } + > + First item + + + A button + + } + > + Second item + + + ), + }, + parameters: { + controls: {sort: 'requiredFirst'}, + }, +}; + +export const WithAddButton = {}; + +export const WithCustomAddButtonLabel = { + args: { + addButtonLabel: 'Custom add button label', + }, +}; + +export const WithoutAddbutton = { + args: { + onAddItem: undefined, + }, +}; diff --git a/src/components/EditGrid/EditGridButtonGroup.js b/src/components/EditGrid/EditGridButtonGroup.js new file mode 100644 index 000000000..2e4210c10 --- /dev/null +++ b/src/components/EditGrid/EditGridButtonGroup.js @@ -0,0 +1,13 @@ +import {ButtonGroup} from '@utrecht/component-library-react'; +import PropTypes from 'prop-types'; +import React from 'react'; + +const EditGridButtonGroup = ({children}) => ( + {children} +); + +EditGridButtonGroup.propTypes = { + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, +}; + +export default EditGridButtonGroup; diff --git a/src/components/EditGrid/EditGridItem.js b/src/components/EditGrid/EditGridItem.js new file mode 100644 index 000000000..492dfdac5 --- /dev/null +++ b/src/components/EditGrid/EditGridItem.js @@ -0,0 +1,21 @@ +import {Fieldset, FieldsetLegend} from '@utrecht/component-library-react'; +import PropTypes from 'prop-types'; +import React from 'react'; + +const EditGridItem = ({heading, children: body, buttons}) => { + return ( +
+ {heading} + {body} + {buttons} +
+ ); +}; + +EditGridItem.propTypes = { + heading: PropTypes.node.isRequired, + children: PropTypes.node, + buttons: PropTypes.node, +}; + +export default EditGridItem; diff --git a/src/components/EditGrid/EditGridItem.stories.js b/src/components/EditGrid/EditGridItem.stories.js new file mode 100644 index 000000000..d63ceca57 --- /dev/null +++ b/src/components/EditGrid/EditGridItem.stories.js @@ -0,0 +1,30 @@ +import Body from 'components/Body'; +import {OFButton} from 'components/Button'; + +import {EditGridButtonGroup, EditGridItem as EditGridItemComponent} from '.'; + +export default { + title: 'Pure React components / EditGrid / Item', + component: EditGridItemComponent, + argTypes: { + children: {control: false}, + buttons: {control: false}, + }, + args: { + heading: 'Item heading', + children: Item body, typically form fields, + buttons: ( + + Save + + Remove + + + ), + }, + parameters: { + controls: {sort: 'requiredFirst'}, + }, +}; + +export const Item = {}; diff --git a/src/components/EditGrid/design-tokens.mdx b/src/components/EditGrid/design-tokens.mdx new file mode 100644 index 000000000..db76170c1 --- /dev/null +++ b/src/components/EditGrid/design-tokens.mdx @@ -0,0 +1,73 @@ +import ofTokens from '@open-formulieren/design-tokens/dist/tokens'; +import {Meta} from '@storybook/addon-docs'; +import utrechtTokens from '@utrecht/design-tokens/dist/tokens'; +import TokensTable from 'design-token-editor/lib/esm/TokensTable'; + + + +## Available design tokens + +**Open Forms specific tokens** + + + +**Utrecht tokens** + +Under the hood, the following components & tokens are used & can be leveraged to change the +appearance of the editgrid component. + +- `utrecht.button-group` +- `utrecht.form-fieldset` + +## Updating your theme + +Before SDK 1.6, the CSS for edit grid was hardcoded. With 1.6, this is now parametrized, but there +are no backwards compatible defaults set. To recover the original styles, use the following tokens: + +```json +{ + "of": { + "editgrid": { + "line-height": {"value": 1.5}, + "gap": {"value": "12px"}, + + "item": { + "gap": {"value": "12px"}, + "border": {"value": "solid 1px {of.color.border}"}, + "padding-block-end": {"value": "24px"}, + "padding-block-start": {"value": "24px"}, + "padding-inline-end": {"value": "24px"}, + "padding-inline-start": {"value": "24px"} + }, + + "item-heading": { + "font-family": {"value": "{of.typography.sans-serif.font-family}"}, + "font-size": {"value": "0.875rem"}, + "line-height": {"value": "1.2"}, + "margin-block-end": {"value": "24px"} + } + } + } +} +``` + +or in CSS: + +```css +.your-theme { + --of-editgrid-item-border: solid 1px var(--of-color-border); + --of-editgrid-item-gap: 12px; + --of-editgrid-item-padding-block-end: 24px; + --of-editgrid-item-padding-block-start: 24px; + --of-editgrid-item-padding-inline-start: 24px; + --of-editgrid-item-padding-inline-end: 24px; + + --of-editgrid-item-heading-font-family: var(--of-typography-sans-serif-font-family); + --of-editgrid-item-heading-font-size: 0.875rem; + --of-editgrid-item-heading-line-height: 1.2; + --of-editgrid-item-heading-margin-block-end: 24px; + + --of-editgrid-line-height: 1.5; + --of-editgrid-gap: 12p; +} +``` diff --git a/src/components/EditGrid/index.js b/src/components/EditGrid/index.js new file mode 100644 index 000000000..9f94e8f0b --- /dev/null +++ b/src/components/EditGrid/index.js @@ -0,0 +1,3 @@ +export {default as EditGridButtonGroup} from './EditGridButtonGroup'; +export {default as EditGridItem} from './EditGridItem'; +export {default as EditGrid} from './EditGrid'; diff --git a/src/scss/components/_editgrid.scss b/src/scss/components/_editgrid.scss index 86398ab2d..9b4a14987 100644 --- a/src/scss/components/_editgrid.scss +++ b/src/scss/components/_editgrid.scss @@ -1,42 +1,54 @@ -@import '~microscope-sass/lib/color'; -@import '~microscope-sass/lib/typography'; - -@import '../mixins/prefix'; - -.#{prefix(editgrid)} { - @include body; - - ul { - padding: 0; - } - - &__group { - padding: $grid-margin-4; - border-style: solid; - border-width: thin; - border-color: $color-border; - - &:not(:first-child) { - border-top-style: none; +/** + * The Utrecht community components do not seem to have a ready-to-use component + * for the edit grid layout, so we can't shake of some custom CSS yet. + */ +@use 'microscope-sass/lib/bem'; + +.openforms-editgrid { + line-height: var(--of-editgrid-line-height, var(--utrecht-document-line-height)); + + display: flex; + flex-direction: column; + gap: var(--of-editgrid-gap); + + @include bem.element('item') { + // NL DS markup uses a field with nested fieldset element inside + .utrecht-form-fieldset__fieldset { + display: flex; + flex-direction: column; + gap: var(--of-editgrid-item-gap); } - } - &__group-label { - @include h4; - padding-bottom: $grid-margin-4; - } + border: var(--of-editgrid-item-border); + max-inline-size: var(--of-editgrid-item-max-inline-size); + padding-block-end: var(--of-editgrid-item-padding-block-end); + padding-block-start: var(--of-editgrid-item-padding-block-start); + padding-inline-end: var(--of-editgrid-item-padding-inline-end); + padding-inline-start: var(--of-editgrid-item-padding-inline-start); - &__list { - list-style: none; - } - - &__group-actions { - padding-top: $grid-margin-2; + // ensure borders are 'collapsed' + & + .openforms-editgrid__item { + border-block-start: none; + } } - &__add-button { - display: flex; - width: 100%; - padding: $grid-margin-2 0; + @include bem.element('item-heading') { + font-family: var( + --of-editgrid-item-heading-font-family, + var(--utrecht-form-fieldset-legend-font-family, var(--utrecht-document-font-family)) + ); + font-size: var( + --of-editgrid-item-heading-font-size, + var(--utrecht-form-fieldset-legend-font-size) + ); + line-height: var( + --of-editgrid-item-heading-line-height, + var(--utrecht-form-fieldset-legend-line-height) + ); + // display: contents was considered, but that doesn't allow specifying different + // margins/paddings at the bottom to create extra space :( + // legend element doesn't care about the fieldset itself having display: flex, so we + // must treat this specially + margin-block-end: var(--of-editgrid-item-heading-margin-block-end, var(--of-editgrid-item-gap)); } } From ed258dde333e9245a44fd90b3094cdeef0fa9a96 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Tue, 31 Oct 2023 11:44:30 +0100 Subject: [PATCH 3/5] :recycle: [#454] Organize button group styles in more centralized location --- src/scss/components/_button-group.scss | 23 ++++++++++++++++++++ src/scss/components/_language-selection.scss | 1 - src/styles.scss | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/scss/components/_button-group.scss diff --git a/src/scss/components/_button-group.scss b/src/scss/components/_button-group.scss new file mode 100644 index 000000000..bcc15b13f --- /dev/null +++ b/src/scss/components/_button-group.scss @@ -0,0 +1,23 @@ +/** + * Extensions on the Utrecht button-group community component. + */ +@use 'microscope-sass/lib/bem'; + +@import '@utrecht/components/button-group/css/index'; + +.utrecht-button-group { + // A button group used in an edit grid item + @include bem.modifier('openforms-editgrid') { + // there currently does not exist a design token in the upstream component, and + // the alignment is also contextual, so we opt for an open-forms extension that + // can be tweaked with proprietary design tokens. + justify-content: var(--of-button-group-editgrid-justify-content, flex-end); + } +} + +// Reference: https://nl-design-system.github.io/utrecht/storybook/?path=/docs/css_css-button-group--docs +@include mobile-only { + .utrecht-button-group { + @include utrecht-button-group--vertical; + } +} diff --git a/src/scss/components/_language-selection.scss b/src/scss/components/_language-selection.scss index 3859fb71a..39b900f33 100644 --- a/src/scss/components/_language-selection.scss +++ b/src/scss/components/_language-selection.scss @@ -1,7 +1,6 @@ @import '~microscope-sass/lib/typography'; @import '@utrecht/components/dist/alternate-lang-nav/css/index.css'; -@import '@utrecht/components/dist/button-group/css/index.css'; /** * Allow using different spacing rules for this specific component diff --git a/src/styles.scss b/src/styles.scss index 1bc9e1e28..aa85986e3 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -24,6 +24,7 @@ @import './scss/components/alert'; @import './scss/components/anchor'; @import './scss/components/button'; +@import './scss/components/button-group'; @import './scss/components/card'; @import './scss/components/content'; @import './scss/components/errors'; From 5d13afff460c7139b514e8b29b6723c97f95bed0 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Tue, 31 Oct 2023 11:45:35 +0100 Subject: [PATCH 4/5] :recycle: [#454] Refactor edit grid usage to new component/markup * Replaced the ChooseProductStep React component to use the new EditGrid component * Updated the templates for formio edit grid to emit valid markup and make use of our edit grid component classes --- .../appointments/steps/ChooseProductStep.js | 98 +++++++++---------- src/formio/templates/editGrid.ejs | 65 +++++++----- src/formio/templates/editGridRow.ejs | 34 +++---- 3 files changed, 100 insertions(+), 97 deletions(-) diff --git a/src/components/appointments/steps/ChooseProductStep.js b/src/components/appointments/steps/ChooseProductStep.js index f31f299c8..50691eb86 100644 --- a/src/components/appointments/steps/ChooseProductStep.js +++ b/src/components/appointments/steps/ChooseProductStep.js @@ -10,11 +10,10 @@ import {toFormikValidationSchema} from 'zod-formik-adapter'; import {OFButton} from 'components/Button'; import {CardTitle} from 'components/Card'; +import {EditGrid, EditGridButtonGroup, EditGridItem} from 'components/EditGrid'; import FAIcon from 'components/FAIcon'; -import {Toolbar, ToolbarList} from 'components/Toolbar'; import useQuery from 'hooks/useQuery'; import useTitle from 'hooks/useTitle'; -import {getBEMClassName} from 'utils'; import {AppointmentConfigContext} from '../Context'; import {useCreateAppointmentContext} from '../CreateAppointment/CreateAppointmentState'; @@ -61,37 +60,34 @@ const ChooseProductStepFields = ({values: {products = []}, validateForm}) => {
{arrayHelpers => ( -
-
- {products.map(({productId}, index) => ( - // blank blocks don't have a product selected yet -> so the index is added - // to make the key guaranteed unique - arrayHelpers.remove(index))} - > - - - ))} -
- - {supportsMultipleProducts && ( -
- arrayHelpers.push({productId: '', amount: 1}))} - > - - - -
- )} -
+ + {' '} + + + } + onAddItem={ + supportsMultipleProducts && + withValidate(() => arrayHelpers.push({productId: '', amount: 1})) + } + > + {products.map(({productId}, index) => ( + // blank blocks don't have a product selected yet -> so the index is added + // to make the key guaranteed unique + arrayHelpers.remove(index))} + > + + + ))} + )}
@@ -118,31 +114,31 @@ const ProductWrapper = ({index, numProducts, onRemove, children}) => { if (!supportsMultipleProducts) { return <>{children}; } + + const buttonRow = numProducts > 1 && ( + + + + + + ); + return ( -
-
+ -
- + } + buttons={buttonRow} + > {children} - - {numProducts > 1 && ( - - - - - - - - )} -
+ ); }; diff --git a/src/formio/templates/editGrid.ejs b/src/formio/templates/editGrid.ejs index f8ab8125d..040e04309 100644 --- a/src/formio/templates/editGrid.ejs +++ b/src/formio/templates/editGrid.ejs @@ -1,42 +1,53 @@ -
-
-
    - {% ctx.rows.forEach(function(row, rowIndex) { %} -
    - {% if (!!ctx.component.groupLabel) { %}
    {{ ctx.t(ctx.component.groupLabel, { _userInput: true }) }} {{ rowIndex + 1 }}
    {% } %} -
  • - {{row}} +
    + {% if(ctx.rows.length) { %} +
    + {% ctx.rows.forEach(function(row, rowIndex) { %} +
    +
    + {% if (!!ctx.component.groupLabel) { %} + + {{ ctx.t(ctx.component.groupLabel, { _userInput: true }) }} {{ rowIndex + 1 }} + + {% } %} + +
    {{row}}
    {% if (ctx.openRows[rowIndex] && !ctx.readOnly) { %} -
    -
      -
    • - -
    • +

      + + + {% if (ctx.component.removeRow) { %} -

    • - -
    • + {% } %} -
    -
    +

    {% } %} -
    +
    {{ctx.errors[rowIndex]}}
    -
  • + +
    - {% }) %} -
+ + {% }) %}
+ {% } %} {% if (!ctx.readOnly && ctx.hasAddButton) { %} -
- -
+

+ +

{% } %}
diff --git a/src/formio/templates/editGridRow.ejs b/src/formio/templates/editGridRow.ejs index c3c5b0c74..c36967b55 100644 --- a/src/formio/templates/editGridRow.ejs +++ b/src/formio/templates/editGridRow.ejs @@ -1,20 +1,16 @@ -
- {% for (const key in ctx.flattenedComponents) { %} - {% if (ctx.isVisibleInRow(ctx.flattenedComponents[key])) { %} -
{{ctx.flattenedComponents[key].label}}: {{ ctx.getView(ctx.flattenedComponents[key], ctx.row[key]) }}
- {% } %} - {% } %} - - {% if (!ctx.self.options.readOnly) { %} -
-
    -
  • - -
  • -
  • - -
  • -
-
+ {% for (const key in ctx.flattenedComponents) { %} + {% if (ctx.isVisibleInRow(ctx.flattenedComponents[key])) { %} +
{{ctx.flattenedComponents[key].label}}: {{ ctx.getView(ctx.flattenedComponents[key], ctx.row[key]) }}
+ {% } %} {% } %} -
+ +{% if (!ctx.self.options.readOnly) { %} +

+ + +

+{% } %} From d59125d521cc7e1b2d052fc4768cd8a97ef9d904 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Tue, 31 Oct 2023 11:46:48 +0100 Subject: [PATCH 5/5] :globe_with_meridians: [#454] Update translations --- src/i18n/compiled/en.json | 6 ++++++ src/i18n/compiled/nl.json | 6 ++++++ src/i18n/messages/en.json | 5 +++++ src/i18n/messages/nl.json | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/src/i18n/compiled/en.json b/src/i18n/compiled/en.json index f617a4153..3934b1d00 100644 --- a/src/i18n/compiled/en.json +++ b/src/i18n/compiled/en.json @@ -1175,6 +1175,12 @@ "value": "Your payment is received and processed." } ], + "cKFCTI": [ + { + "type": 0, + "value": "Add another" + } + ], "cxDC/G": [ { "type": 0, diff --git a/src/i18n/compiled/nl.json b/src/i18n/compiled/nl.json index c3d19a3e3..02671942d 100644 --- a/src/i18n/compiled/nl.json +++ b/src/i18n/compiled/nl.json @@ -1179,6 +1179,12 @@ "value": "Uw betaling is ontvangen en verwerkt." } ], + "cKFCTI": [ + { + "type": 0, + "value": "Nog één toevoegen" + } + ], "cxDC/G": [ { "type": 0, diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 7777efebd..03e72a82a 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -544,6 +544,11 @@ "description": "payment registered status", "originalDefault": "Your payment is received and processed." }, + "cKFCTI": { + "defaultMessage": "Add another", + "description": "Edit grid add button, default label text", + "originalDefault": "Add another" + }, "cxDC/G": { "defaultMessage": "The required field is not filled out.", "description": "ZOD 'required' error message", diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index 36cd05c53..af0937c2b 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -550,6 +550,11 @@ "description": "payment registered status", "originalDefault": "Your payment is received and processed." }, + "cKFCTI": { + "defaultMessage": "Nog één toevoegen", + "description": "Edit grid add button, default label text", + "originalDefault": "Add another" + }, "cxDC/G": { "defaultMessage": "Het verplichte veld is niet ingevuld.", "description": "ZOD 'required' error message",