@byline/admin 6.0.1 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/modules/admin-preferences/index.d.ts +7 -3
  2. package/dist/modules/admin-preferences/index.js +1 -1
  3. package/dist/modules/admin-preferences/schemas.d.ts +19 -16
  4. package/dist/modules/admin-preferences/schemas.js +46 -2
  5. package/dist/modules/analytics/components/timeseries.d.ts +28 -0
  6. package/dist/modules/analytics/components/timeseries.js +107 -35
  7. package/dist/modules/analytics/components/timeseries.module.js +15 -0
  8. package/dist/modules/analytics/components/timeseries_module.css +110 -3
  9. package/package.json +7 -7
  10. package/src/modules/admin-preferences/index.ts +7 -2
  11. package/src/modules/admin-preferences/schemas.test.node.ts +74 -0
  12. package/src/modules/admin-preferences/schemas.ts +85 -8
  13. package/src/modules/analytics/components/dashboard.test.node.ts +51 -0
  14. package/src/modules/analytics/components/timeseries.module.css +176 -3
  15. package/src/modules/analytics/components/timeseries.tsx +153 -40
  16. package/dist/exports-parity.test.node.js +0 -37
  17. package/dist/fields/array/array-field.jsx +0 -209
  18. package/dist/fields/blocks/blocks-field.jsx +0 -248
  19. package/dist/fields/checkbox/checkbox-field.jsx +0 -24
  20. package/dist/fields/code/code-editor.jsx +0 -211
  21. package/dist/fields/code/code-field.jsx +0 -109
  22. package/dist/fields/column-formatter.jsx +0 -25
  23. package/dist/fields/date-time-formatter.jsx +0 -18
  24. package/dist/fields/datetime/datetime-field.jsx +0 -22
  25. package/dist/fields/draggable-context-menu.jsx +0 -50
  26. package/dist/fields/field-admin.test.node.js +0 -40
  27. package/dist/fields/field-renderer.jsx +0 -136
  28. package/dist/fields/field-services-context.jsx +0 -17
  29. package/dist/fields/file/file-field.jsx +0 -176
  30. package/dist/fields/file/file-upload-field.jsx +0 -89
  31. package/dist/fields/group/group-field.jsx +0 -46
  32. package/dist/fields/image/image-field.jsx +0 -183
  33. package/dist/fields/image/image-upload-field.jsx +0 -157
  34. package/dist/fields/local-date-time.jsx +0 -69
  35. package/dist/fields/locale-badge.jsx +0 -19
  36. package/dist/fields/numerical/numerical-field.jsx +0 -93
  37. package/dist/fields/relation/relation-column-formatter.jsx +0 -74
  38. package/dist/fields/relation/relation-display.jsx +0 -118
  39. package/dist/fields/relation/relation-field.jsx +0 -108
  40. package/dist/fields/relation/relation-many-field.jsx +0 -142
  41. package/dist/fields/relation/relation-picker.jsx +0 -240
  42. package/dist/fields/relation/relation-summary.jsx +0 -45
  43. package/dist/fields/select/select-field.jsx +0 -27
  44. package/dist/fields/sortable-item.jsx +0 -54
  45. package/dist/fields/text/text-field.jsx +0 -79
  46. package/dist/fields/text-area/text-area-field.jsx +0 -79
  47. package/dist/forms/available-locales-reconcile.test.node.js +0 -43
  48. package/dist/forms/available-locales-widget.jsx +0 -63
  49. package/dist/forms/document-actions.jsx +0 -486
  50. package/dist/forms/form-context.jsx +0 -598
  51. package/dist/forms/form-modals.jsx +0 -128
  52. package/dist/forms/form-renderer.jsx +0 -532
  53. package/dist/forms/form-status-display.jsx +0 -69
  54. package/dist/forms/navigation-guard.jsx +0 -68
  55. package/dist/forms/nested-path.test.node.js +0 -115
  56. package/dist/forms/path-widget.jsx +0 -125
  57. package/dist/forms/pending-uploads.test.node.js +0 -19
  58. package/dist/forms/repeating-items.test.node.js +0 -29
  59. package/dist/forms/scheduled-publication-control.jsx +0 -389
  60. package/dist/forms/scheduled-publication-state.test.node.js +0 -121
  61. package/dist/forms/scheduled-publication-time.test.node.js +0 -70
  62. package/dist/forms/status-transitions.test.node.js +0 -76
  63. package/dist/forms/tree-placement-widget.jsx +0 -160
  64. package/dist/forms/upload-executor.test.node.js +0 -368
  65. package/dist/forms/use-form-layout.test.node.js +0 -69
  66. package/dist/modules/admin-account/components/change-password.jsx +0 -169
  67. package/dist/modules/admin-account/components/container.jsx +0 -164
  68. package/dist/modules/admin-account/components/preferences.jsx +0 -148
  69. package/dist/modules/admin-account/components/theme-switch.jsx +0 -52
  70. package/dist/modules/admin-account/components/update.jsx +0 -186
  71. package/dist/modules/admin-permissions/components/inspector.jsx +0 -200
  72. package/dist/modules/admin-preferences/schemas.test.node.js +0 -48
  73. package/dist/modules/admin-roles/components/create.jsx +0 -155
  74. package/dist/modules/admin-roles/components/permissions.jsx +0 -239
  75. package/dist/modules/admin-roles/components/update.jsx +0 -148
  76. package/dist/modules/admin-users/components/create.jsx +0 -206
  77. package/dist/modules/admin-users/components/roles.jsx +0 -126
  78. package/dist/modules/admin-users/components/set-password.jsx +0 -133
  79. package/dist/modules/admin-users/components/update.jsx +0 -215
  80. package/dist/modules/analytics/components/dashboard.jsx +0 -208
  81. package/dist/modules/analytics/components/dashboard.test.node.js +0 -162
  82. package/dist/modules/analytics/components/timeseries.jsx +0 -193
  83. package/dist/modules/auth/components/sign-in-form.jsx +0 -89
  84. package/dist/modules/auth/safe-redirect.test.node.js +0 -40
  85. package/dist/modules/auth/sign-in-form-props.test.node.js +0 -7
  86. package/dist/presentation/group.jsx +0 -26
  87. package/dist/presentation/row.jsx +0 -23
  88. package/dist/presentation/tabs.jsx +0 -39
  89. package/dist/services/admin-services-context.jsx +0 -17
  90. package/dist/widgets/diff-viewer/diff-modal.jsx +0 -110
  91. package/dist/widgets/source-locale-badge/source-locale-badge.jsx +0 -28
  92. package/dist/widgets/status-badge/status-badge.jsx +0 -46
@@ -1,27 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { ErrorText, Label, Select } from '@byline/ui/react';
9
- import cx from 'clsx';
10
- import { useFieldError, useFieldValue, useIsDirty } from '../../forms/form-context';
11
- import styles from './select-field.module.css';
12
- export const SelectField = ({ field, value, defaultValue, onChange, id, path, }) => {
13
- const fieldPath = path ?? field.name;
14
- const fieldError = useFieldError(fieldPath);
15
- const isDirty = useIsDirty(fieldPath);
16
- const fieldValue = useFieldValue(fieldPath);
17
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
18
- const htmlId = id ?? fieldPath;
19
- return (<div className={`byline-field-select ${field.name}`}>
20
- {field.label && (<Label id={htmlId} htmlFor={htmlId} label={field.label} required={!field.optional} className={cx('byline-field-select-label', styles.label)}/>)}
21
- <Select size="xs" variant="outlined" id={htmlId} name={field.name} placeholder="Select an option" required={!field.optional} value={incomingValue} ariaLabel={field.label} helpText={field.helpText} items={field.options.map((opt) => ({ value: opt.value, label: opt.label }))} onValueChange={(value) => {
22
- if (value != null)
23
- onChange?.(value);
24
- }} className={cx(isDirty && ['byline-field-select-dirty', styles.dirty])}/>
25
- {fieldError && <ErrorText id={`${field.name}-error`} text={fieldError}/>}
26
- </div>);
27
- };
@@ -1,54 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useState } from 'react';
9
- import { useTranslation } from '@byline/i18n/react';
10
- import { ChevronDownIcon, GripperVerticalIcon, useSortable } from '@byline/ui/react';
11
- import cx from 'clsx';
12
- import { DraggableContextMenu } from './draggable-context-menu';
13
- import styles from './sortable-item.module.css';
14
- const ItemFrame = ({ label, children, onAddBelow, onRemove, grip, rootRef, style, dragging = false, rootClassName, }) => {
15
- const { t } = useTranslation('byline-admin');
16
- const [collapsed, setCollapsed] = useState(false);
17
- return (<div ref={rootRef} style={style} className={cx('byline-sortable', styles.root, rootClassName, dragging && ['byline-sortable-dragging', styles.dragging], collapsed && ['byline-sortable-collapsed', styles.collapsed])}>
18
- <div className={cx('byline-sortable-header', styles.header, !collapsed && ['byline-sortable-header-expanded', styles['header-expanded']])}>
19
- {grip}
20
- <div className={cx('byline-sortable-label', styles.label)}>{label}</div>
21
- <DraggableContextMenu onAddBelow={onAddBelow} onRemove={onRemove}/>
22
- <button type="button" className={cx('byline-sortable-toggle', styles.toggle)} onClick={() => setCollapsed((prev) => !prev)} aria-label={collapsed
23
- ? t('fields.sortable.expandAriaLabel')
24
- : t('fields.sortable.collapseAriaLabel')}>
25
- <ChevronDownIcon className={cx('byline-sortable-toggle-icon', styles['toggle-icon'], collapsed && ['byline-sortable-toggle-icon-rotated', styles['toggle-icon-rotated']])}/>
26
- </button>
27
- </div>
28
- <div className={cx('byline-sortable-content', styles.content, collapsed && ['byline-sortable-content-hidden', styles['content-hidden']])}>
29
- {children}
30
- </div>
31
- </div>);
32
- };
33
- export const SortableItem = ({ id, label, children, onAddBelow, onRemove, }) => {
34
- const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
35
- id,
36
- transition: {
37
- duration: 250,
38
- easing: 'cubic-bezier(0, 0.2, 0.2, 1)',
39
- },
40
- });
41
- const style = {
42
- transform: transform ? `translate3d(${transform.x}px, ${transform.y}px, 0)` : undefined,
43
- transition,
44
- zIndex: isDragging ? 10 : 'auto',
45
- };
46
- return (<ItemFrame label={label} onAddBelow={onAddBelow} onRemove={onRemove} rootRef={setNodeRef} style={style} dragging={isDragging} grip={<button type="button" className={cx('byline-sortable-grip', styles.grip)} {...attributes} {...listeners}>
47
- <GripperVerticalIcon className={cx('byline-sortable-grip-icon', styles['grip-icon'])}/>
48
- </button>}>
49
- {children}
50
- </ItemFrame>);
51
- };
52
- export const StaticItem = ({ label, children, onAddBelow, onRemove, }) => (<ItemFrame label={label} onAddBelow={onAddBelow} onRemove={onRemove} rootClassName="byline-sortable-static">
53
- {children}
54
- </ItemFrame>);
@@ -1,79 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useCallback } from 'react';
9
- import { Input, Label } from '@byline/ui/react';
10
- import cx from 'clsx';
11
- import { useFieldError, useFieldValue } from '../../forms/form-context';
12
- import { LocaleBadge } from '../locale-badge';
13
- import styles from './text-field.module.css';
14
- export const TextField = ({ field, value, defaultValue, onChange, id, path, locale, components, }) => {
15
- const fieldPath = path ?? field.name;
16
- const fieldError = useFieldError(fieldPath);
17
- const fieldValue = useFieldValue(fieldPath);
18
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
19
- const htmlId = id ?? fieldPath;
20
- const handleChange = useCallback((value) => {
21
- if (onChange) {
22
- onChange(value);
23
- }
24
- }, [onChange]);
25
- // Custom component slots (from admin config)
26
- const slots = components;
27
- const CustomLabel = slots?.Label;
28
- const CustomHelpText = slots?.HelpText;
29
- const CustomField = slots?.Field;
30
- const BeforeField = slots?.beforeField;
31
- const AfterField = slots?.afterField;
32
- // Shared props available to every slot component
33
- const slotBaseProps = {
34
- field: field,
35
- path: fieldPath,
36
- value: incomingValue,
37
- error: fieldError,
38
- id: htmlId,
39
- };
40
- // When a locale is active, render a custom Label+badge and suppress the
41
- // Input's own label so the locale indicator appears in the label row.
42
- const showBadge = !!locale && !!field.label;
43
- // Determine whether the label is handled externally (by a custom slot or
44
- // the locale badge row) so Input doesn't render its own.
45
- const hasCustomLabel = !!CustomLabel;
46
- const suppressInputLabel = showBadge || hasCustomLabel;
47
- const suppressInputHelpText = !!CustomHelpText;
48
- const labelRowClass = cx('byline-field-text-label-row', styles['label-row']);
49
- // ── Label rendering ──────────────────────────────────────────
50
- const renderLabel = () => {
51
- if (hasCustomLabel) {
52
- return (<div className={labelRowClass}>
53
- <CustomLabel {...slotBaseProps} label={field.label} required={!field.optional}/>
54
- {showBadge && <LocaleBadge locale={locale}/>}
55
- </div>);
56
- }
57
- if (showBadge) {
58
- return (<div className={labelRowClass}>
59
- <Label id={`${htmlId}-label`} htmlFor={htmlId} label={field.label} required={!field.optional}/>
60
- <LocaleBadge locale={locale}/>
61
- </div>);
62
- }
63
- return null;
64
- };
65
- // ── Field input rendering ────────────────────────────────────
66
- const renderInput = () => {
67
- if (CustomField) {
68
- return (<CustomField {...slotBaseProps} onChange={handleChange} defaultValue={defaultValue} placeholder={field.placeholder}/>);
69
- }
70
- return (<Input id={htmlId} name={field.name} label={suppressInputLabel ? undefined : field.label} required={!field.optional} helpText={suppressInputHelpText ? undefined : field.helpText} value={incomingValue} onChange={(e) => handleChange(e.target.value)} error={fieldError != null} errorText={fieldError}/>);
71
- };
72
- return (<div className={`byline-field-text ${field.name}`}>
73
- {renderLabel()}
74
- {BeforeField && <BeforeField {...slotBaseProps}/>}
75
- {renderInput()}
76
- {AfterField && <AfterField {...slotBaseProps}/>}
77
- {CustomHelpText && <CustomHelpText {...slotBaseProps} helpText={field.helpText}/>}
78
- </div>);
79
- };
@@ -1,79 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useCallback } from 'react';
9
- import { Label, TextArea } from '@byline/ui/react';
10
- import cx from 'clsx';
11
- import { useFieldError, useFieldValue } from '../../forms/form-context';
12
- import { LocaleBadge } from '../locale-badge';
13
- import styles from './text-area-field.module.css';
14
- export const TextAreaField = ({ field, value, defaultValue, onChange, id, path, locale, components, }) => {
15
- const fieldPath = path ?? field.name;
16
- const fieldError = useFieldError(fieldPath);
17
- const fieldValue = useFieldValue(fieldPath);
18
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
19
- const htmlId = id ?? fieldPath;
20
- const handleChange = useCallback((value) => {
21
- if (onChange) {
22
- onChange(value);
23
- }
24
- }, [onChange]);
25
- // Custom component slots (from admin config)
26
- const slots = components;
27
- const CustomLabel = slots?.Label;
28
- const CustomHelpText = slots?.HelpText;
29
- const CustomField = slots?.Field;
30
- const BeforeField = slots?.beforeField;
31
- const AfterField = slots?.afterField;
32
- // Shared props available to every slot component
33
- const slotBaseProps = {
34
- field: field,
35
- path: fieldPath,
36
- value: incomingValue,
37
- error: fieldError,
38
- id: htmlId,
39
- };
40
- // When a locale is active, render a custom Label+badge and suppress the
41
- // TextArea's own label so the locale indicator appears in the label row.
42
- const showBadge = !!locale && !!field.label;
43
- // Determine whether the label is handled externally (by a custom slot or
44
- // the locale badge row) so TextArea doesn't render its own.
45
- const hasCustomLabel = !!CustomLabel;
46
- const suppressInputLabel = showBadge || hasCustomLabel;
47
- const suppressInputHelpText = !!CustomHelpText;
48
- const labelRowClass = cx('byline-field-text-area-label-row', styles['label-row']);
49
- // ── Label rendering ──────────────────────────────────────────
50
- const renderLabel = () => {
51
- if (hasCustomLabel) {
52
- return (<div className={labelRowClass}>
53
- <CustomLabel {...slotBaseProps} label={field.label} required={!field.optional}/>
54
- {showBadge && <LocaleBadge locale={locale}/>}
55
- </div>);
56
- }
57
- if (showBadge) {
58
- return (<div className={labelRowClass}>
59
- <Label id={`${htmlId}-label`} htmlFor={htmlId} label={field.label} required={!field.optional}/>
60
- <LocaleBadge locale={locale}/>
61
- </div>);
62
- }
63
- return null;
64
- };
65
- // ── Field input rendering ────────────────────────────────────
66
- const renderInput = () => {
67
- if (CustomField) {
68
- return (<CustomField {...slotBaseProps} onChange={handleChange} defaultValue={defaultValue} placeholder={field.placeholder}/>);
69
- }
70
- return (<TextArea id={htmlId} name={field.name} label={suppressInputLabel ? undefined : field.label} required={!field.optional} helpText={suppressInputHelpText ? undefined : field.helpText} value={incomingValue} onChange={(e) => handleChange(e.target.value)} error={fieldError != null} errorText={fieldError} rows={4}/>);
71
- };
72
- return (<div className={`byline-field-text-area ${field.name}`}>
73
- {renderLabel()}
74
- {BeforeField && <BeforeField {...slotBaseProps}/>}
75
- {renderInput()}
76
- {AfterField && <AfterField {...slotBaseProps}/>}
77
- {CustomHelpText && <CustomHelpText {...slotBaseProps} helpText={field.helpText}/>}
78
- </div>);
79
- };
@@ -1,43 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { describe, expect, it } from 'vitest';
9
- import { reconcileLocaleState } from './available-locales-reconcile.js';
10
- /**
11
- * The widget's reconciliation is expressed purely as Checkbox intent +
12
- * disabled state — no per-row text. `reconcileLocaleState` is the pure heart
13
- * of that mapping; the four cells below are the full truth table from
14
- * docs/08-internationalization/index.md.
15
- */
16
- describe('reconcileLocaleState', () => {
17
- it('ledger-complete + advertised → green, enabled (advertised & complete)', () => {
18
- expect(reconcileLocaleState(true, true)).toEqual({ intent: 'success', disabled: false });
19
- });
20
- it('ledger-complete + not advertised → green, enabled (invitation to advertise)', () => {
21
- expect(reconcileLocaleState(false, true)).toEqual({ intent: 'success', disabled: false });
22
- });
23
- it('not complete + advertised → amber, enabled (advertising an incomplete locale)', () => {
24
- // The ⚠ row: surfaced as `warning` and kept enabled so the editor can
25
- // uncheck to resolve the over-advertisement.
26
- expect(reconcileLocaleState(true, false)).toEqual({ intent: 'warning', disabled: false });
27
- });
28
- it('not complete + not advertised → gray, disabled (nothing to advertise)', () => {
29
- expect(reconcileLocaleState(false, false)).toEqual({ intent: 'noeffect', disabled: true });
30
- });
31
- it('completeness drives green regardless of advertised state', () => {
32
- // The ledger fact wins the colour when complete; only the disabled/amber
33
- // distinction depends on the advertised flag.
34
- expect(reconcileLocaleState(true, true).intent).toBe('success');
35
- expect(reconcileLocaleState(false, true).intent).toBe('success');
36
- });
37
- it('only the not-complete + not-advertised cell is non-interactive', () => {
38
- expect(reconcileLocaleState(true, true).disabled).toBe(false);
39
- expect(reconcileLocaleState(false, true).disabled).toBe(false);
40
- expect(reconcileLocaleState(true, false).disabled).toBe(false);
41
- expect(reconcileLocaleState(false, false).disabled).toBe(true);
42
- });
43
- });
@@ -1,63 +0,0 @@
1
- 'use client';
2
- /**
3
- * This Source Code is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
- *
7
- * Copyright (c) Infonomic Company Limited
8
- */
9
- import { useCallback, useMemo } from 'react';
10
- import { useTranslation } from '@byline/i18n/react';
11
- import { Checkbox, Label } from '@byline/ui/react';
12
- import cx from 'clsx';
13
- import { reconcileLocaleState } from './available-locales-reconcile';
14
- import styles from './available-locales-widget.module.css';
15
- import { useFormContext, useSystemAvailableLocales } from './form-context';
16
- export { reconcileLocaleState } from './available-locales-reconcile';
17
- /**
18
- * System-managed `availableLocales` widget — the editorial "advertise these
19
- * content locales" control. Renders one checkbox per content locale in the
20
- * sidebar, directly below the path widget. The checked state reflects the
21
- * stored advertised set (`useSystemAvailableLocales`); each row's intent
22
- * reconciles that against the structural ledger fact (`availableVersionLocales`)
23
- * via {@link reconcileLocaleState}. Opt-in: nothing is advertised until the
24
- * editor checks a (green) locale.
25
- *
26
- * Stable override handles: `.byline-form-available-locales`,
27
- * `.byline-form-available-locales-list`.
28
- */
29
- export const AvailableLocalesWidget = ({ disabled: mutationsBlocked = false, contentLocales, availableVersionLocales, }) => {
30
- const { t } = useTranslation('byline-admin');
31
- const { setSystemAvailableLocales } = useFormContext();
32
- const advertised = useSystemAvailableLocales();
33
- const advertisedSet = useMemo(() => new Set(advertised), [advertised]);
34
- const ledgerSet = useMemo(() => new Set(availableVersionLocales), [availableVersionLocales]);
35
- const toggle = useCallback((code, checked) => {
36
- if (mutationsBlocked)
37
- return;
38
- const next = new Set(advertised);
39
- if (checked) {
40
- next.add(code);
41
- }
42
- else {
43
- next.delete(code);
44
- }
45
- setSystemAvailableLocales([...next]);
46
- }, [mutationsBlocked, advertised, setSystemAvailableLocales]);
47
- if (contentLocales.length === 0) {
48
- return null;
49
- }
50
- return (<div className={cx('byline-form-available-locales', styles.container)}>
51
- <Label id="available-locales" htmlFor="available-locales-group" label={t('availableLocalesWidget.label')}/>
52
- <div id="available-locales-group" className={cx('byline-form-available-locales-list', styles.list)} role="group" aria-labelledby="label-for-available-locales" aria-describedby="available-locales-description">
53
- {contentLocales.map(({ code, label }) => {
54
- const checked = advertisedSet.has(code);
55
- const { intent, disabled } = reconcileLocaleState(checked, ledgerSet.has(code));
56
- return (<Checkbox key={code} id={`available-locale-${code}`} name={`__availableLocale_${code}__`} label={label} intent={intent} checked={checked} disabled={mutationsBlocked || disabled} onCheckedChange={(value) => toggle(code, value === true)}/>);
57
- })}
58
- </div>
59
- <span id="available-locales-description" className={cx('byline-form-available-locales-sr-only', styles['sr-only'])}>
60
- {t('availableLocalesWidget.srDescription')}
61
- </span>
62
- </div>);
63
- };