diff --git a/.nvmrc b/.nvmrc index f4abeaade7e..be43e15fce8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.* +12.* diff --git a/README.md b/README.md index 2adfecbe2d9..9e3a13ec5d4 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ email = [ADOBE EMAIL] The auth key can be obtained by following the steps listed [here](https://www.jfrog.com/confluence/display/RTF/Npm+Registry#npmRegistry-UsingBasicAuthentication). Sample cURL: ``` - curl -u danilu: https://artifactory.corp.adobe.com/artifactory/api/npm/auth + curl -u : https://artifactory.corp.adobe.com/artifactory/api/npm/auth ``` Then you should be able to install with npm: diff --git a/lint-packages.js b/lint-packages.js index 2f771f6981b..2d39f495203 100644 --- a/lint-packages.js +++ b/lint-packages.js @@ -1,22 +1,53 @@ const glob = require('fast-glob'); const fs = require('fs'); const assert = require('assert'); +const chalk = require('chalk'); let packages = glob.sync(__dirname + '/packages/@react-{aria,spectrum,stately}/*/package.json'); +let errors = false; + +// soft assert won't fail the whole thing, allowing us to accumulate all errors at once +// there's probably a nicer way to do this, but well... sometimes it's good enough. feel free to update me :) +// maybe turn me into an actual eslint plugin so we get their format for styling +function softAssert(val, message) { + try { + assert(val, message); + } catch { + console.error(chalk.red(message)); + errors = true; + } +} +softAssert.deepEqual = function (val, val2, message) { + try { + assert.deepEqual(val, val2, message); + } catch { + console.error(chalk.red(message)); + errors = true; + } +} +softAssert.equal = function (val, val2, message) { + try { + assert.equal(val, val2, message); + } catch { + console.error(chalk.red(message)); + errors = true; + } +} + for (let pkg of packages) { let json = JSON.parse(fs.readFileSync(pkg)); - assert(json.main, `${pkg} did not have "main"`); - assert(json.module, `${pkg} did not have "module"`); - assert(json.types, `${pkg} did not have "types"`); - assert(json.source, `${pkg} did not have "source"`); - assert.deepEqual(json.files, ['dist'], `${pkg} did not match "files"`); - assert.equal(json.sideEffects, false, `${pkg} is missing sideEffects: false`); - assert(!json.dependencies || !json.dependencies['@adobe/spectrum-css-temp'], `${pkg} has @adobe/spectrum-css-temp in dependencies instead of devDependencies`); - assert(json.dependencies && json.dependencies['@babel/runtime'], `${pkg} is missing a dependency on @babel/runtime`); + softAssert(json.main, `${pkg} did not have "main"`); + softAssert(json.module, `${pkg} did not have "module"`); + softAssert(json.types, `${pkg} did not have "types"`); + softAssert(json.source, `${pkg} did not have "source"`); + softAssert.deepEqual(json.files, ['dist'], `${pkg} did not match "files"`); + softAssert.equal(json.sideEffects, false, `${pkg} is missing sideEffects: false`); + softAssert(!json.dependencies || !json.dependencies['@adobe/spectrum-css-temp'], `${pkg} has @adobe/spectrum-css-temp in dependencies instead of devDependencies`); + softAssert(json.dependencies && json.dependencies['@babel/runtime'], `${pkg} is missing a dependency on @babel/runtime`); if (json.name.startsWith('@react-spectrum') && json.devDependencies && json.devDependencies['@adobe/spectrum-css-temp']) { - assert.deepEqual(json.targets, { + softAssert.deepEqual(json.targets, { main: { includeNodeModules: ['@adobe/spectrum-css-temp'] }, @@ -26,5 +57,9 @@ for (let pkg of packages) { }, `${pkg} did not match "targets"`); } - assert(json.publishConfig && json.publishConfig.access === 'public', `${pkg} has missing or incorrect publishConfig`); + softAssert(json.publishConfig && json.publishConfig.access === 'public', `${pkg} has missing or incorrect publishConfig`); +} + +if (errors) { + return process.exit(1); } diff --git a/package.json b/package.json index d9e780b0251..d20baa7f904 100644 --- a/package.json +++ b/package.json @@ -48,12 +48,12 @@ "@react/collection-view": "^4.1.5", "@react/react-spectrum": "^2.24.0", "@react/react-spectrum-icons": "^2.1.0", - "@storybook/addon-a11y": "^5.1.9", - "@storybook/addon-actions": "^5.1.9", - "@storybook/addon-info": "^5.1.9", - "@storybook/addon-knobs": "^5.1.9", - "@storybook/addon-links": "^5.1.9", - "@storybook/react": "^5.1.9", + "@storybook/addon-a11y": "^5.2.1", + "@storybook/addon-actions": "^5.2.1", + "@storybook/addon-info": "^5.2.1", + "@storybook/addon-knobs": "^5.2.1", + "@storybook/addon-links": "^5.2.1", + "@storybook/react": "^5.2.1", "@testing-library/react": "^8.0.1", "@testing-library/user-event": "^4.1.0", "@types/react": "^16.8.0", @@ -69,6 +69,7 @@ "babel-plugin-react-remove-properties": "^0.3.0", "babel-plugin-transform-glob-import": "^1.0.1", "babelify": "^10.0.0", + "chalk": "^2.4.2", "classnames": "^2.2.5", "core-js": "^3.0.0", "css-loader": "^2.1.1", @@ -80,6 +81,7 @@ "eslint-plugin-react": "^7.12.4", "eslint-plugin-react-hooks": "^1.6.0", "eslint-plugin-rulesdir": "^0.1.0", + "fast-glob": "^3.1.0", "file-loader": "^0.9.0", "full-icu": "^1.3.0", "identity-obj-proxy": "^3.0.0", @@ -90,8 +92,6 @@ "lerna": "^3.13.2", "lfcdn": "^0.4.2", "md5": "^2.2.1", - "moment": "^2.15.1", - "moment-range": "^3.0.3", "nyc": "^10.2.0", "parcel": "^2.0.0-alpha.2.1", "plop": "^2.4.0", diff --git a/packages/@adobe/spectrum-css-temp/components/fieldgroup/index.css b/packages/@adobe/spectrum-css-temp/components/fieldgroup/index.css index 2cdd8aeac14..d9411afeeac 100644 --- a/packages/@adobe/spectrum-css-temp/components/fieldgroup/index.css +++ b/packages/@adobe/spectrum-css-temp/components/fieldgroup/index.css @@ -24,6 +24,14 @@ governing permissions and limitations under the License. .spectrum-FieldGroup { display: flex; vertical-align: top; + flex-wrap: wrap; +} + + +.spectrum-FieldGroup-label { + display: flex; + align-items: flex-start; + inline-size: 100%; } /* topdoc diff --git a/packages/@adobe/spectrum-css-temp/components/fieldlabel/index.css b/packages/@adobe/spectrum-css-temp/components/fieldlabel/index.css index 8aec2bd30d2..baf4ca2044c 100644 --- a/packages/@adobe/spectrum-css-temp/components/fieldlabel/index.css +++ b/packages/@adobe/spectrum-css-temp/components/fieldlabel/index.css @@ -25,7 +25,7 @@ governing permissions and limitations under the License. {{ fieldlabel/fieldlabel-required.yml }} */ .spectrum-FieldLabel { - display: block; + display: flex; box-sizing: border-box; @@ -40,6 +40,11 @@ governing permissions and limitations under the License. -webkit-font-smoothing: subpixel-antialiased; -moz-osx-font-smoothing: auto; font-smoothing: subpixel-antialiased; + text-align: start; +} + +.spectrum-FieldLabel-label { + margin-inline-end: 5px; } .spectrum-FieldLabel-requiredIcon { diff --git a/packages/@adobe/spectrum-css-temp/components/radio/index.css b/packages/@adobe/spectrum-css-temp/components/radio/index.css index f877fcb0b68..186d617f7e1 100644 --- a/packages/@adobe/spectrum-css-temp/components/radio/index.css +++ b/packages/@adobe/spectrum-css-temp/components/radio/index.css @@ -29,10 +29,10 @@ governing permissions and limitations under the License. position: relative; - min-height: var(--spectrum-radio-height); - max-width: 100%; + min-block-size: var(--spectrum-radio-height); + max-inline-size: 100%; - margin-right: calc(var(--spectrum-radio-cursor-hit-x) * 2); + margin-inline-end: calc(var(--spectrum-radio-cursor-hit-x) * 2); vertical-align: top; } @@ -59,8 +59,10 @@ governing permissions and limitations under the License. position: absolute; top: 0; left: calc(var(--spectrum-radio-cursor-hit-x) * -1); - width: calc(100% + var(--spectrum-radio-cursor-hit-x) * 2); - height: 100%; + /* right wins in rtl because inline-size is specified */ + right: calc(var(--spectrum-radio-cursor-hit-x) * -1); + inline-size: calc(100% + var(--spectrum-radio-cursor-hit-x) * 2); + block-size: 100%; opacity: .0001; z-index: 1; @@ -77,20 +79,20 @@ governing permissions and limitations under the License. } .spectrum-Radio-label { - margin-left: var(--spectrum-radio-text-gap); + margin-inline-start: var(--spectrum-radio-text-gap); font-size: var(--spectrum-radio-text-size); transition: color var(--spectrum-global-animation-duration-100) ease-in-out; /* Hardcoded as no good way to calculate this */ - margin-top: var(--spectrum-radio-label-margin-top); + margin-block-start: var(--spectrum-radio-label-margin-top); } .spectrum-Radio-button { position: relative; box-sizing: border-box; - width: var(--spectrum-radio-circle-diameter); - height: var(--spectrum-radio-circle-diameter); + inline-size: var(--spectrum-radio-circle-diameter); + block-size: var(--spectrum-radio-circle-diameter); /* Fix vertical alignment when not wrapping since we're flex-start */ margin: calc((var(--spectrum-radio-height) - var(--spectrum-radio-circle-diameter)) / 2) 0; @@ -109,7 +111,7 @@ governing permissions and limitations under the License. display: inline-flex; flex-direction: column; align-items: center; - height: var(--spectrum-radio-labelbelow-height); + block-size: var(--spectrum-radio-labelbelow-height); .spectrum-Radio-button { flex-shrink: 0; diff --git a/packages/@adobe/spectrum-css-temp/components/textfield/index.css b/packages/@adobe/spectrum-css-temp/components/textfield/index.css index ca8c1c4fa9b..33de3fadcd2 100644 --- a/packages/@adobe/spectrum-css-temp/components/textfield/index.css +++ b/packages/@adobe/spectrum-css-temp/components/textfield/index.css @@ -194,7 +194,7 @@ governing permissions and limitations under the License. display: block; position: absolute; inset-inline-start: 12px; - top: calc(calc(var(--spectrum-textfield-height) / 2) - calc(var(--spectrum-icon-magnifier-width) / 2)); + top: calc(calc(var(--spectrum-textfield-height) / 2) - calc(var(--spectrum-icon-magnifier-width) / 2) - calc(var(--spectrum-textfield-padding-bottom) - var(--spectrum-textfield-padding-top))); } /* styles the textfield properly if the left icon is provided */ diff --git a/packages/@react-aria/radio/index.ts b/packages/@react-aria/radio/index.ts new file mode 100644 index 00000000000..8420b1093fd --- /dev/null +++ b/packages/@react-aria/radio/index.ts @@ -0,0 +1 @@ +export * from './src'; diff --git a/packages/@react-aria/radio/package.json b/packages/@react-aria/radio/package.json new file mode 100644 index 00000000000..399cd6e1683 --- /dev/null +++ b/packages/@react-aria/radio/package.json @@ -0,0 +1,30 @@ +{ + "name": "@react-aria/radio", + "version": "3.0.0-alpha.1", + "description": "Spectrum UI components in React", + "main": "dist/main.js", + "module": "dist/module.js", + "types": "dist/types.d.ts", + "source": "src/index.ts", + "files": [ + "dist" + ], + "sideEffects": false, + "repository": { + "type": "git", + "url": "https://github.com/adobe/react-spectrum" + }, + "dependencies": { + "@babel/runtime": "^7.6.2", + "@react-types/radio": "^3.0.0-alpha.1", + "@react-aria/utils": "^3.0.0-alpha.1", + "@react-stately/radio": "^3.0.0-alpha.1" + }, + "peerDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/@react-aria/radio/src/index.ts b/packages/@react-aria/radio/src/index.ts new file mode 100644 index 00000000000..8ee16cbb0d0 --- /dev/null +++ b/packages/@react-aria/radio/src/index.ts @@ -0,0 +1,2 @@ +export * from './useRadio'; +export * from './useRadioGroup'; diff --git a/packages/@react-aria/radio/src/useRadio.ts b/packages/@react-aria/radio/src/useRadio.ts new file mode 100644 index 00000000000..212554d06bb --- /dev/null +++ b/packages/@react-aria/radio/src/useRadio.ts @@ -0,0 +1,54 @@ +import {AllHTMLAttributes} from 'react'; +import {LabelPosition, RadioProps} from '@react-types/radio'; +import {RadioGroupState} from '@react-stately/radio'; + +interface RadioAriaProps extends RadioProps { + isDisabled?: boolean, + isRequired?: boolean, + isReadOnly?: boolean, + isEmphasized?: boolean, + labelPosition?: LabelPosition, + name?: string, + validationState?: 'valid' | 'invalid', + selectedRadio?: string, + setSelectedRadio?: (value: string) => void +} + +interface RadioAria { + inputProps: AllHTMLAttributes +} + +export function useRadio(props: RadioAriaProps, state: RadioGroupState): RadioAria { + let { + value, + isRequired, + isReadOnly, + isDisabled, + name + } = props; + let { + selectedRadio, + setSelectedRadio + } = state; + + let checked = selectedRadio === value; + + let onChange = (e) => { + e.stopPropagation(); + + setSelectedRadio(value); + }; + + return { + inputProps: { + type: 'radio', + name, + disabled: isDisabled, + readOnly: isReadOnly, + required: isRequired, + checked, + 'aria-checked': checked, + onChange + } + }; +} diff --git a/packages/@react-aria/radio/src/useRadioGroup.ts b/packages/@react-aria/radio/src/useRadioGroup.ts new file mode 100644 index 00000000000..79255d6b68a --- /dev/null +++ b/packages/@react-aria/radio/src/useRadioGroup.ts @@ -0,0 +1,25 @@ +import {AllHTMLAttributes} from 'react'; +import {RadioGroupProps} from '@react-types/radio'; +import {useId} from '@react-aria/utils'; + + +interface RadioGroupAria { + radioGroupProps: AllHTMLAttributes, + radioProps: AllHTMLAttributes +} + +export function useRadioGroup(props: RadioGroupProps): RadioGroupAria { + let defaultGroupId = `${useId()}-group`; + let { + name = defaultGroupId + } = props; + + return { + radioGroupProps: { + role: 'radiogroup' + }, + radioProps: { + name + } + }; +} diff --git a/packages/@react-aria/searchfield/package.json b/packages/@react-aria/searchfield/package.json index 907118ed586..8875600d155 100644 --- a/packages/@react-aria/searchfield/package.json +++ b/packages/@react-aria/searchfield/package.json @@ -18,6 +18,7 @@ "@babel/runtime": "^7.6.2", "@react-aria/i18n": "^3.0.0-alpha.1", "@react-aria/utils": "^3.0.0-alpha.1", + "@react-stately/searchfield": "^3.0.0-alpha.1", "@react-types/searchfield": "^3.0.0-alpha.1" }, "peerDependencies": { diff --git a/packages/@react-aria/searchfield/src/useSearchField.ts b/packages/@react-aria/searchfield/src/useSearchField.ts index 74e17d30099..89aaa8856c7 100644 --- a/packages/@react-aria/searchfield/src/useSearchField.ts +++ b/packages/@react-aria/searchfield/src/useSearchField.ts @@ -1,7 +1,8 @@ import {AllHTMLAttributes, RefObject} from 'react'; import {chain} from '@react-aria/utils'; import intlMessages from './intl/*.json'; -import {SearchFieldProps, SearchFieldState} from '@react-types/searchfield'; +import {SearchFieldProps} from '@react-types/searchfield'; +import {SearchFieldState} from '@react-stately/searchfield'; import {useMessageFormatter} from '@react-aria/i18n'; interface SearchFieldAria { @@ -11,7 +12,7 @@ interface SearchFieldAria { } export function useSearchField( - props: SearchFieldProps, + props: SearchFieldProps, state: SearchFieldState, searchFieldRef: RefObject ): SearchFieldAria { @@ -50,7 +51,7 @@ export function useSearchField( state.setValue('', e); searchFieldRef.current.focus(); }; - + return { searchDivProps: { role diff --git a/packages/@react-aria/textfield/src/useTextField.ts b/packages/@react-aria/textfield/src/useTextField.ts index a1f67eb5681..ee8f039d44b 100644 --- a/packages/@react-aria/textfield/src/useTextField.ts +++ b/packages/@react-aria/textfield/src/useTextField.ts @@ -1,13 +1,12 @@ import {AllHTMLAttributes, ChangeEvent} from 'react'; -import {TextFieldProps, TextFieldState} from '@react-types/textfield'; +import {TextFieldProps} from '@react-types/textfield'; interface TextFieldAria { textFieldProps: AllHTMLAttributes } export function useTextField( - props: TextFieldProps, - state: TextFieldState + props: TextFieldProps ): TextFieldAria { let { isDisabled = false, @@ -15,18 +14,18 @@ export function useTextField( isReadOnly = false, autoFocus = false, validationState, - type = 'text' + type = 'text', + onChange = () => {} } = props; return { textFieldProps: { type, disabled: isDisabled, - required: isRequired, readOnly: isReadOnly, + 'aria-required': isRequired || undefined, 'aria-invalid': validationState === 'invalid' || undefined, - onChange: (e: ChangeEvent) => state.setValue(e.target.value, e), - value: state.value, + onChange: (e: ChangeEvent) => onChange(e.target.value, e), autoFocus } }; diff --git a/packages/@react-aria/textfield/test/useTextField.test.js b/packages/@react-aria/textfield/test/useTextField.test.js index aa9fc593424..57d4b45b372 100644 --- a/packages/@react-aria/textfield/test/useTextField.test.js +++ b/packages/@react-aria/textfield/test/useTextField.test.js @@ -3,33 +3,20 @@ import {renderHook} from 'react-hooks-testing-library'; import {useTextField} from '../'; describe('useTextField hook', () => { - let state = {}; - let setValue = jest.fn(); - let renderTextFieldHook = (props) => { - let {result} = renderHook(() => useTextField(props, state)); + let {result} = renderHook(() => useTextField(props)); return result.current.textFieldProps; }; - beforeEach(() => { - state.value = ''; - state.setValue = setValue; - }); - - afterEach(() => { - setValue.mockClear(); - }); - describe('should return textFieldProps', () => { it('with default textfield props if no props are provided', () => { let props = renderTextFieldHook({}); expect(props.type).toBe('text'); expect(props.disabled).toBeFalsy(); - expect(props.required).toBeFalsy(); expect(props.readOnly).toBeFalsy(); expect(props['aria-invalid']).toBeUndefined(); + expect(props['aria-required']).toBeUndefined(); expect(typeof props.onChange).toBe('function'); - expect(props.value).toBe(state.value); expect(props.autoFocus).toBeFalsy(); }); @@ -49,10 +36,10 @@ describe('useTextField hook', () => { it('with appropriate props if isRequired is defined', () => { let props = renderTextFieldHook({isRequired: true}); - expect(props.required).toBeTruthy(); + expect(props['aria-required']).toBeTruthy(); props = renderTextFieldHook({isRequired: false}); - expect(props.required).toBeFalsy(); + expect(props['aria-required']).toBeUndefined(); }); it('with appropriate props if isReadOnly is defined', () => { @@ -79,8 +66,9 @@ describe('useTextField hook', () => { expect(props.autoFocus).toBeFalsy(); }); - it('with an onChange that sets the state value when called', () => { - let props = renderTextFieldHook({}); + it('with an onChange that calls user specified onChange with appropriate values', () => { + let onChange = jest.fn(); + let props = renderTextFieldHook({onChange}); let mockEvent = { target: { value: 1 @@ -88,8 +76,9 @@ describe('useTextField hook', () => { }; props.onChange(mockEvent); - expect(state.setValue).toHaveBeenCalledTimes(1); - expect(state.setValue).toHaveBeenCalledWith(mockEvent.target.value, mockEvent); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith(mockEvent.target.value, mockEvent); + onChange.mockClear(); }); }); }); diff --git a/packages/@react-aria/utils/src/useId.ts b/packages/@react-aria/utils/src/useId.ts index cc71168686c..3605314c791 100644 --- a/packages/@react-aria/utils/src/useId.ts +++ b/packages/@react-aria/utils/src/useId.ts @@ -3,9 +3,12 @@ import {useMemo, useState} from 'react'; let map: Map void> = new Map(); let id = 0; +// don't want to conflict with ids from v2, this will guarantee something unique +// plus we'll know how many instances of this module are loaded on a page if there are more than one number ;) +let randomInstanceNumber = Math.round(Math.random() * 10000000000); export function useId(defaultId?: string): string { let [value, setValue] = useState(defaultId); - let res = useMemo(() => value || `react-spectrum-${++id}`, [value]); + let res = useMemo(() => value || `react-spectrum-${randomInstanceNumber}-${++id}`, [value]); map.set(res, setValue); return res; } diff --git a/packages/@react-spectrum/checkbox/src/Checkbox.tsx b/packages/@react-spectrum/checkbox/src/Checkbox.tsx index 77d05933f10..a2c4e93039e 100644 --- a/packages/@react-spectrum/checkbox/src/Checkbox.tsx +++ b/packages/@react-spectrum/checkbox/src/Checkbox.tsx @@ -22,6 +22,7 @@ export const Checkbox = forwardRef((props: CheckboxProps, ref: RefObject diff --git a/packages/@react-spectrum/dialog/stories/DialogTrigger.stories.tsx b/packages/@react-spectrum/dialog/stories/DialogTrigger.stories.tsx index 6b8fea08c62..bd95ba986ac 100644 --- a/packages/@react-spectrum/dialog/stories/DialogTrigger.stories.tsx +++ b/packages/@react-spectrum/dialog/stories/DialogTrigger.stories.tsx @@ -85,7 +85,7 @@ storiesOf('DialogTrigger', module) 'popover inside scroll view', () => (
-
+
Trigger diff --git a/packages/@react-spectrum/form/intl/cs-CZ.json b/packages/@react-spectrum/form/intl/cs-CZ.json new file mode 100644 index 00000000000..cdff792c43b --- /dev/null +++ b/packages/@react-spectrum/form/intl/cs-CZ.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(volitelně)", + "(required)": "(požadováno)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/da-DK.json b/packages/@react-spectrum/form/intl/da-DK.json new file mode 100644 index 00000000000..3fc201cfb9b --- /dev/null +++ b/packages/@react-spectrum/form/intl/da-DK.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(valgfrit)", + "(required)": "(påkrævet)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/de-DE.json b/packages/@react-spectrum/form/intl/de-DE.json new file mode 100644 index 00000000000..889b26170e4 --- /dev/null +++ b/packages/@react-spectrum/form/intl/de-DE.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(optional)", + "(required)": "(erforderlich)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/en-US.json b/packages/@react-spectrum/form/intl/en-US.json new file mode 100644 index 00000000000..640d7139e8c --- /dev/null +++ b/packages/@react-spectrum/form/intl/en-US.json @@ -0,0 +1,4 @@ +{ + "(required)": "(required)", + "(optional)": "(optional)" +} diff --git a/packages/@react-spectrum/form/intl/es-ES.json b/packages/@react-spectrum/form/intl/es-ES.json new file mode 100644 index 00000000000..02a187ffe8c --- /dev/null +++ b/packages/@react-spectrum/form/intl/es-ES.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(opcional)", + "(required)": "(necesario)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/fi-FI.json b/packages/@react-spectrum/form/intl/fi-FI.json new file mode 100644 index 00000000000..b1abb4391e0 --- /dev/null +++ b/packages/@react-spectrum/form/intl/fi-FI.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(valinnainen)", + "(required)": "(pakollinen)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/fr-FR.json b/packages/@react-spectrum/form/intl/fr-FR.json new file mode 100644 index 00000000000..7ab2d2cdbeb --- /dev/null +++ b/packages/@react-spectrum/form/intl/fr-FR.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(facultatif)", + "(required)": "(requis)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/hu-HU.json b/packages/@react-spectrum/form/intl/hu-HU.json new file mode 100644 index 00000000000..9a01bda512e --- /dev/null +++ b/packages/@react-spectrum/form/intl/hu-HU.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(opcionális)", + "(required)": "(kötelező)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/it-IT.json b/packages/@react-spectrum/form/intl/it-IT.json new file mode 100644 index 00000000000..150e22f8c8d --- /dev/null +++ b/packages/@react-spectrum/form/intl/it-IT.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(facoltativo)", + "(required)": "(obbligatorio)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/ja-JP.json b/packages/@react-spectrum/form/intl/ja-JP.json new file mode 100644 index 00000000000..523d558fd96 --- /dev/null +++ b/packages/@react-spectrum/form/intl/ja-JP.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(オプション)", + "(required)": "(必須)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/ko-KR.json b/packages/@react-spectrum/form/intl/ko-KR.json new file mode 100644 index 00000000000..ee49539af01 --- /dev/null +++ b/packages/@react-spectrum/form/intl/ko-KR.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(선택 사항)", + "(required)": "(필수 사항)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/nb-NO.json b/packages/@react-spectrum/form/intl/nb-NO.json new file mode 100644 index 00000000000..9bebc3b7879 --- /dev/null +++ b/packages/@react-spectrum/form/intl/nb-NO.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(valgfritt)", + "(required)": "(obligatorisk)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/nl-NL.json b/packages/@react-spectrum/form/intl/nl-NL.json new file mode 100644 index 00000000000..34b5d0ee39e --- /dev/null +++ b/packages/@react-spectrum/form/intl/nl-NL.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(optioneel)", + "(required)": "(vereist)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/pl-PL.json b/packages/@react-spectrum/form/intl/pl-PL.json new file mode 100644 index 00000000000..d10f75339ba --- /dev/null +++ b/packages/@react-spectrum/form/intl/pl-PL.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(opcjonalne)", + "(required)": "(wymagane)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/pt-BR.json b/packages/@react-spectrum/form/intl/pt-BR.json new file mode 100644 index 00000000000..a54dd33b58d --- /dev/null +++ b/packages/@react-spectrum/form/intl/pt-BR.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(opcional)", + "(required)": "(obrigatório)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/ru-RU.json b/packages/@react-spectrum/form/intl/ru-RU.json new file mode 100644 index 00000000000..9de37ae9287 --- /dev/null +++ b/packages/@react-spectrum/form/intl/ru-RU.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(дополнительно)", + "(required)": "(требуется)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/sv-SE.json b/packages/@react-spectrum/form/intl/sv-SE.json new file mode 100644 index 00000000000..dd282d3c3be --- /dev/null +++ b/packages/@react-spectrum/form/intl/sv-SE.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(valfritt)", + "(required)": "(krävs)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/tr-TR.json b/packages/@react-spectrum/form/intl/tr-TR.json new file mode 100644 index 00000000000..09e30719f6f --- /dev/null +++ b/packages/@react-spectrum/form/intl/tr-TR.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(isteğe bağlı)", + "(required)": "(gerekli)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/uk-UA.json b/packages/@react-spectrum/form/intl/uk-UA.json new file mode 100644 index 00000000000..7dcfaa7b9f8 --- /dev/null +++ b/packages/@react-spectrum/form/intl/uk-UA.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(необов’язково)", + "(required)": "(обов’язково)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/zh-CN.json b/packages/@react-spectrum/form/intl/zh-CN.json new file mode 100644 index 00000000000..926128fbbda --- /dev/null +++ b/packages/@react-spectrum/form/intl/zh-CN.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(可选)", + "(required)": "(必填)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/intl/zh-TW.json b/packages/@react-spectrum/form/intl/zh-TW.json new file mode 100644 index 00000000000..5bc2bece965 --- /dev/null +++ b/packages/@react-spectrum/form/intl/zh-TW.json @@ -0,0 +1,4 @@ +{ + "(optional)": "(選填)", + "(required)": "(必填)" +} \ No newline at end of file diff --git a/packages/@react-spectrum/form/package.json b/packages/@react-spectrum/form/package.json index 94f5345a0e1..81fa0e70d8f 100644 --- a/packages/@react-spectrum/form/package.json +++ b/packages/@react-spectrum/form/package.json @@ -28,11 +28,12 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", + "@react-aria/i18n": "^3.0.0-alpha.1", "@react-aria/label": "^3.0.0-alpha.1", "@react-spectrum/utils": "^3.0.0-alpha.1", "@react-types/label": "^3.0.0-alpha.1", "@react-types/shared": "^3.0.0-alpha.1", - "classnames": "^2.2.5" + "@spectrum-icons/ui": "^3.0.0-alpha.1" }, "devDependencies": { "@adobe/spectrum-css-temp": "^3.0.0-alpha.1" diff --git a/packages/@react-spectrum/form/src/FieldLabel.tsx b/packages/@react-spectrum/form/src/FieldLabel.tsx new file mode 100644 index 00000000000..9a0337bebe5 --- /dev/null +++ b/packages/@react-spectrum/form/src/FieldLabel.tsx @@ -0,0 +1,29 @@ +import {classNames} from '@react-spectrum/utils'; +import {FieldLabelProps} from './types'; +import {LabelBase} from './LabelBase'; +import React, {forwardRef, RefObject} from 'react'; +import styles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css'; + +export const FieldLabel = forwardRef(({label, labelAlign, labelFor, className, children, ...otherProps}: FieldLabelProps, ref: RefObject & RefObject) => { + let labelClassNames = classNames( + styles, + 'spectrum-FieldLabel', + { + 'spectrum-FieldLabel--alignStart': labelAlign === 'start', + 'spectrum-FieldLabel--alignEnd': labelAlign === 'end' + } + ); + + return ( + + {children} + + ); +}); diff --git a/packages/@react-spectrum/form/src/FormItem.tsx b/packages/@react-spectrum/form/src/FormItem.tsx index dd657704b6c..4d831ab2083 100644 --- a/packages/@react-spectrum/form/src/FormItem.tsx +++ b/packages/@react-spectrum/form/src/FormItem.tsx @@ -18,6 +18,7 @@ export const FormItem = forwardRef(({label, labelAlign = 'start', labelFor, clas return ( {children} diff --git a/packages/@react-spectrum/form/src/LabelBase.tsx b/packages/@react-spectrum/form/src/LabelBase.tsx index 5d7bfdf7439..e84eba89aa2 100644 --- a/packages/@react-spectrum/form/src/LabelBase.tsx +++ b/packages/@react-spectrum/form/src/LabelBase.tsx @@ -1,14 +1,17 @@ -import classNames from 'classnames'; +import Asterisk from '@spectrum-icons/ui/Asterisk'; +import {classNames} from '@react-spectrum/utils'; +import {FieldLabelBase} from './types'; import {filterDOMProps} from '@react-spectrum/utils'; -import {LabelProps} from '@react-types/label'; -import React, {forwardRef, ReactNode, RefObject} from 'react'; +import intlMessages from '../intl/*.json'; +import React, {forwardRef, RefObject} from 'react'; +import styles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css'; import {useLabel} from '@react-aria/label'; +import {useMessageFormatter} from '@react-aria/i18n'; -interface SpectrumLabelBaseProps extends LabelProps { - labelClassName: string, - wrapperClassName: string, - componentName: string, - icon?: ReactNode +interface SpectrumLabelBaseProps extends FieldLabelBase { + labelClassName?: string, + wrapperClassName?: string, + componentName?: string } export const LabelBase = forwardRef((props: SpectrumLabelBaseProps, ref: RefObject & RefObject) => { @@ -16,9 +19,8 @@ export const LabelBase = forwardRef((props: SpectrumLabelBaseProps, ref: RefObje There are 3 cases: 1. No children - only render the