diff --git a/src/components/views/elements/StyledRadioGroup.tsx b/src/components/views/elements/StyledRadioGroup.tsx index 1c3f5c10cd0..f8c52bd3b2b 100644 --- a/src/components/views/elements/StyledRadioGroup.tsx +++ b/src/components/views/elements/StyledRadioGroup.tsx @@ -52,20 +52,25 @@ function StyledRadioGroup({ }; return - { definitions.map(d => - - { d.label } - - { d.description ? { d.description } : null } - ) } + { definitions.map(d => { + const id = `${name}-${d.value}`; + return ( + + { d.label } + + { d.description ? { d.description } : null } + ); + }) } ; } diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index 152578d4995..de4cccf5a44 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { ReactNode } from "react"; import { IJoinRuleEventContent, JoinRule, RestrictedAllowType } from "matrix-js-sdk/src/@types/partials"; import { Room } from "matrix-js-sdk/src/models/room"; import { EventType } from "matrix-js-sdk/src/@types/event"; @@ -40,9 +40,10 @@ interface IProps { closeSettingsFn(): void; onError(error: Error): void; beforeChange?(joinRule: JoinRule): Promise; // if returns false then aborts the change + aliasWarning?: ReactNode; } -const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSettingsFn }: IProps) => { +const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeChange, closeSettingsFn }: IProps) => { const cli = room.client; const restrictedRoomCapabilities = SpaceStore.instance.restrictedJoinRuleSupport; @@ -90,7 +91,10 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet }, { value: JoinRule.Public, label: _t("Public"), - description: _t("Anyone can find and join."), + description: <> + { _t("Anyone can find and join.") } + { aliasWarning } + , }]; if (roomSupportsRestricted || preferredRestrictionVersion || joinRule === JoinRule.Restricted) { diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index faa6d0eb6e5..19345b83598 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -270,14 +270,13 @@ export default class SecurityRoomSettingsTab extends React.Component - { aliasWarning } - ; } diff --git a/test/components/views/elements/StyledRadioGroup-test.tsx b/test/components/views/elements/StyledRadioGroup-test.tsx new file mode 100644 index 00000000000..b82920b92b9 --- /dev/null +++ b/test/components/views/elements/StyledRadioGroup-test.tsx @@ -0,0 +1,115 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import "../../../skinned-sdk"; + +import React from 'react'; +import { mount } from 'enzyme'; +import { act } from "react-dom/test-utils"; + +import StyledRadioGroup from "../../../../src/components/views/elements/StyledRadioGroup"; + +describe('', () => { + const optionA = { + value: 'Anteater', + label: Anteater label, + description: 'anteater description', + className: 'a-class', + }; + const optionB = { + value: 'Badger', + label: Badger label, + }; + const optionC = { + value: 'Canary', + label: Canary label, + description: Canary description, + }; + const defaultDefinitions = [optionA, optionB, optionC]; + const defaultProps = { + name: 'test', + className: 'test-class', + definitions: defaultDefinitions, + onChange: jest.fn(), + }; + const getComponent = (props = {}) => mount(); + + const getInputByValue = (component, value) => component.find(`input[value="${value}"]`); + const getCheckedInput = component => component.find('input[checked=true]'); + + it('renders radios correctly when no value is provided', () => { + const component = getComponent(); + + expect(component).toMatchSnapshot(); + expect(getCheckedInput(component).length).toBeFalsy(); + }); + + it('selects correct button when value is provided', () => { + const component = getComponent({ + value: optionC.value, + }); + + expect(getCheckedInput(component).at(0).props().value).toEqual(optionC.value); + }); + + it('selects correct buttons when definitions have checked prop', () => { + const definitions = [ + { ...optionA, checked: true }, + optionB, + { ...optionC, checked: false }, + ]; + const component = getComponent({ + value: optionC.value, definitions, + }); + + expect(getInputByValue(component, optionA.value).props().checked).toBeTruthy(); + expect(getInputByValue(component, optionB.value).props().checked).toBeFalsy(); + // optionC.checked = false overrides value matching + expect(getInputByValue(component, optionC.value).props().checked).toBeFalsy(); + }); + + it('disables individual buttons based on definition.disabled', () => { + const definitions = [ + optionA, + { ...optionB, disabled: true }, + { ...optionC, disabled: true }, + ]; + const component = getComponent({ definitions }); + expect(getInputByValue(component, optionA.value).props().disabled).toBeFalsy(); + expect(getInputByValue(component, optionB.value).props().disabled).toBeTruthy(); + expect(getInputByValue(component, optionC.value).props().disabled).toBeTruthy(); + }); + + it('disables all buttons with disabled prop', () => { + const component = getComponent({ disabled: true }); + expect(getInputByValue(component, optionA.value).props().disabled).toBeTruthy(); + expect(getInputByValue(component, optionB.value).props().disabled).toBeTruthy(); + expect(getInputByValue(component, optionC.value).props().disabled).toBeTruthy(); + }); + + it('calls onChange on click', () => { + const onChange = jest.fn(); + const component = getComponent({ + value: optionC.value, + onChange, + }); + + act(() => { + getInputByValue(component, optionB.value).simulate('change'); + }); + + expect(onChange).toHaveBeenCalledWith(optionB.value); + }); +}); diff --git a/test/components/views/elements/__snapshots__/StyledRadioGroup-test.tsx.snap b/test/components/views/elements/__snapshots__/StyledRadioGroup-test.tsx.snap new file mode 100644 index 00000000000..423c006a727 --- /dev/null +++ b/test/components/views/elements/__snapshots__/StyledRadioGroup-test.tsx.snap @@ -0,0 +1,158 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders radios correctly when no value is provided 1`] = ` + + Anteater label + , + "value": "Anteater", + }, + Object { + "label": + Badger label + , + "value": "Badger", + }, + Object { + "description": + Canary description + , + "label": + Canary label + , + "value": "Canary", + }, + ] + } + name="test" + onChange={[MockFunction]} +> + +