diff --git a/docs/en-US/component/radio.md b/docs/en-US/component/radio.md index b5082544b6..bcd13cf266 100644 --- a/docs/en-US/component/radio.md +++ b/docs/en-US/component/radio.md @@ -61,6 +61,14 @@ radio/radio-button-group ::: +## Options attribute ^(2.11.2) + +:::demo Shortcut from basic `el-radio-group` usage. You can customize the alias of the `options` through the `props` attribute. + +radio/options + +::: + ## Button style Radio with button styles. @@ -119,18 +127,20 @@ radio/with-borders ### RadioGroup Attributes -| Name | Description | Type | Default | -| --------------------------- | ------------------------------------------------- | ---------------------------------- | ------- | -| model-value / v-model | binding value | ^[string] / ^[number] / ^[boolean] | — | -| size | the size of radio buttons or bordered radios | ^[string] | default | -| disabled | whether the nesting radios are disabled | ^[boolean] | false | -| validate-event | whether to trigger form validation | ^[boolean] | true | -| text-color | font color when button is active | ^[string] | #ffffff | -| fill | border and background color when button is active | ^[string] | #409eff | -| aria-label ^(a11y) ^(2.7.2) | same as `aria-label` in RadioGroup | ^[string] | — | -| name | native `name` attribute | ^[string] | — | -| id | native `id` attribute | ^[string] | — | -| label ^(a11y) ^(deprecated) | same as `aria-label` in RadioGroup | ^[string] | — | +| Name | Description | Type | Default | +| --------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- | +| model-value / v-model | binding value | ^[string] / ^[number] / ^[boolean] | — | +| size | the size of radio buttons or bordered radios | ^[string] | default | +| disabled | whether the nesting radios are disabled | ^[boolean] | false | +| validate-event | whether to trigger form validation | ^[boolean] | true | +| text-color | font color when button is active | ^[string] | #ffffff | +| fill | border and background color when button is active | ^[string] | #409eff | +| aria-label ^(a11y) ^(2.7.2) | same as `aria-label` in RadioGroup | ^[string] | — | +| name | native `name` attribute | ^[string] | — | +| id | native `id` attribute | ^[string] | — | +| label ^(a11y) ^(deprecated) | same as `aria-label` in RadioGroup | ^[string] | — | +| options ^(2.11.2) | data of the options, the key of `value` and `label` and `disabled` can be customize by `props` | ^[array]`Array<{[key: string]: any}>` | — | +| props ^(2.11.2) | configuration options | ^[object]`{ value?: string, label?: string, disabled?: boolean}` | ### RadioGroup Events diff --git a/docs/examples/radio/options.vue b/docs/examples/radio/options.vue new file mode 100644 index 0000000000..e834334468 --- /dev/null +++ b/docs/examples/radio/options.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/components/radio/__tests__/radio.test.tsx b/packages/components/radio/__tests__/radio.test.tsx index 3392a87d7c..f9ae4f14ac 100644 --- a/packages/components/radio/__tests__/radio.test.tsx +++ b/packages/components/radio/__tests__/radio.test.tsx @@ -205,6 +205,77 @@ describe('Radio group', () => { expect(radio.value).toEqual(3) expect(radio1.classes()).toContain('is-active') }) + + it('renders el-radio-group using default option fields', async () => { + const radio = ref(3) + const options = [ + { + value: 3, + label: 'Option A', + }, + { + value: 6, + label: 'Option B', + }, + { + value: 9, + label: 'Option C', + }, + ] + const wrapper = mount(() => ( + + )) + await nextTick() + const [radio1, radio2] = wrapper.findAll('.el-radio') + expect(radio1.classes()).toContain('is-checked') + await radio2.trigger('click') + expect(radio2.classes()).toContain('is-checked') + expect(radio.value).toEqual(6) + }) + + it('renders el-radio-group with custom option fields', async () => { + const radio = ref(3) + const options = [ + { + id: 3, + label: 'Option A', + }, + { + id: 6, + label: 'Option B', + }, + { + id: 9, + label: 'Option C', + }, + ] + const wrapper = mount(() => ( + + )) + await nextTick() + const [radio1, radio2] = wrapper.findAll('.el-radio') + expect(radio1.classes()).toContain('is-checked') + await radio2.trigger('click') + expect(radio2.classes()).toContain('is-checked') + expect(radio.value).toEqual(6) + }) + + it('passes custom attributes from options to el-radio', () => { + const options = [ + { value: 'a', label: 'A', 'data-test': 'custom-attr-1' }, + { value: 'b', label: 'B', 'data-test': 'custom-attr-2' }, + ] + const wrapper = mount(RadioGroup, { + props: { options }, + }) + const [radio1, radio2] = wrapper.findAll('.el-radio') + expect(radio1.attributes('data-test')).toBe('custom-attr-1') + expect(radio2.attributes('data-test')).toBe('custom-attr-2') + }) }) describe('Radio Button', () => { diff --git a/packages/components/radio/src/radio-group.ts b/packages/components/radio/src/radio-group.ts index 990de4b298..26260bf514 100644 --- a/packages/components/radio/src/radio-group.ts +++ b/packages/components/radio/src/radio-group.ts @@ -1,7 +1,8 @@ -import { buildProps } from '@element-plus/utils' +import { buildProps, definePropType } from '@element-plus/utils' import { useAriaProps, useSizeProp } from '@element-plus/hooks' import { radioEmits } from './radio' +import type { RadioPropsPublic } from './radio' import type { ExtractPropTypes, __ExtractPublicPropTypes } from 'vue' import type RadioGroup from './radio-group.vue' @@ -56,6 +57,13 @@ export const radioGroupProps = buildProps({ type: Boolean, default: true, }, + options: { + type: definePropType(Array), + }, + props: { + type: definePropType(Object), + default: () => radioDefaultProps, + }, ...useAriaProps(['ariaLabel']), } as const) export type RadioGroupProps = ExtractPropTypes @@ -66,3 +74,16 @@ export type RadioGroupPropsPublic = __ExtractPublicPropTypes< export const radioGroupEmits = radioEmits export type RadioGroupEmits = typeof radioGroupEmits export type RadioGroupInstance = InstanceType & unknown + +export type radioOption = RadioPropsPublic & Record + +export const radioDefaultProps: Required = { + label: 'label', + value: 'value', + disabled: 'disabled', +} +export type radioOptionProp = { + value?: string + label?: string + disabled?: string +} diff --git a/packages/components/radio/src/radio-group.vue b/packages/components/radio/src/radio-group.vue index 8f3f1aef9a..731dad6f46 100644 --- a/packages/components/radio/src/radio-group.vue +++ b/packages/components/radio/src/radio-group.vue @@ -7,7 +7,13 @@ :aria-label="!isLabeledByFormItem ? ariaLabel || 'radio-group' : undefined" :aria-labelledby="isLabeledByFormItem ? formItem!.labelId : undefined" > - + + + @@ -26,9 +32,14 @@ import { useFormItem, useFormItemInputId } from '@element-plus/components/form' import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants' import { useId, useNamespace } from '@element-plus/hooks' import { debugWarn } from '@element-plus/utils' -import { radioGroupEmits, radioGroupProps } from './radio-group' +import { + radioDefaultProps, + radioGroupEmits, + radioGroupProps, +} from './radio-group' import { radioGroupKey } from './constants' import { isEqual } from 'lodash-unified' +import ElRadio from './radio.vue' import type { RadioGroupProps } from './radio-group' @@ -65,6 +76,19 @@ const name = computed(() => { return props.name || radioId.value }) +const aliasProps = computed(() => ({ + ...radioDefaultProps, + ...props.props, +})) +const getOptionProps = (option: Record) => { + const base = { + label: option[aliasProps.value.label], + value: option[aliasProps.value.value], + disabled: option[aliasProps.value.disabled], + } + return { ...option, ...base } +} + provide( radioGroupKey, reactive({