diff --git a/res/css/_common.scss b/res/css/_common.scss index 29bb318266..e4a5f8ddd0 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -172,6 +172,21 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { outline: none; } +// override defaults +fieldset { + display: inline-block; + margin-inline: unset; + padding-block: unset; + padding-inline: unset; + min-inline-size: unset; + border: none; +} + +legend { + padding-inline: unset; + border: none; +} + // .mx_textinput is a container for a text input // + some other controls like buttons, ... // it has the appearance of a text box so the controls diff --git a/res/css/_components.scss b/res/css/_components.scss index 6e6df9963e..33c17b64e7 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -159,6 +159,7 @@ @import "./views/elements/_RoomAliasField.scss"; @import "./views/elements/_SSOButtons.scss"; @import "./views/elements/_ServerPicker.scss"; +@import "./views/elements/_SettingsFlag.scss"; @import "./views/elements/_Slider.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_StyledCheckbox.scss"; @@ -269,6 +270,7 @@ @import "./views/settings/_SecureBackupPanel.scss"; @import "./views/settings/_SetIdServer.scss"; @import "./views/settings/_SetIntegrationManager.scss"; +@import "./views/settings/_SettingsFieldset.scss"; @import "./views/settings/_SpellCheckLanguages.scss"; @import "./views/settings/_ThemeChoicePanel.scss"; @import "./views/settings/_UpdateCheckButton.scss"; diff --git a/res/css/views/dialogs/_SettingsDialog.scss b/res/css/views/dialogs/_SettingsDialog.scss index b3b6802c3d..f9b373c30a 100644 --- a/res/css/views/dialogs/_SettingsDialog.scss +++ b/res/css/views/dialogs/_SettingsDialog.scss @@ -29,6 +29,8 @@ limitations under the License. box-sizing: border-box; min-width: 580px; padding-right: 100px; + display: flex; + flex-direction: column; // Put some padding on the bottom to avoid the settings tab from // colliding harshly with the dialog when scrolled down. diff --git a/res/css/views/elements/_SettingsFlag.scss b/res/css/views/elements/_SettingsFlag.scss new file mode 100644 index 0000000000..68e324fe81 --- /dev/null +++ b/res/css/views/elements/_SettingsFlag.scss @@ -0,0 +1,43 @@ +/* +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. +*/ + +.mx_SettingsFlag { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + margin-bottom: 4px; + + .mx_ToggleSwitch { + flex: 0 0 auto; + } +} + +.mx_SettingsFlag_label { + flex: 1; + display: flex; + flex-direction: column; + font-size: $font-14px; + color: $primary-content; + padding-right: 10px; +} + +.mx_SettingsFlag_microcopy { + margin-top: 4px; + font-size: $font-12px; + line-height: $font-15px; + color: $secondary-content; +} diff --git a/res/css/views/settings/_SettingsFieldset.scss b/res/css/views/settings/_SettingsFieldset.scss new file mode 100644 index 0000000000..fbb192a4bc --- /dev/null +++ b/res/css/views/settings/_SettingsFieldset.scss @@ -0,0 +1,46 @@ +/* +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. +*/ + +.mx_SettingsFieldset { + margin: 10px 80px 10px 0; + box-sizing: content-box; +} + +.mx_SettingsFieldset_legend { + font-size: $font-16px; + display: block; + font-weight: $font-semi-bold; + color: $primary-content; + margin-bottom: 10px; + margin-top: 12px; +} + +.mx_SettingsFieldset_description { + color: $settings-subsection-fg-color; + font-size: $font-14px; + display: block; + margin-top: 0; + margin-bottom: 10px; + + p { + margin-top: 10px; + margin-bottom: 0; + + &:first-child { + margin: 0; + } + } +} diff --git a/src/components/views/elements/SettingsFlag.tsx b/src/components/views/elements/SettingsFlag.tsx index 0847db801e..b93ee774d7 100644 --- a/src/components/views/elements/SettingsFlag.tsx +++ b/src/components/views/elements/SettingsFlag.tsx @@ -93,16 +93,18 @@ export default class SettingsFlag extends React.Component { } else { return (
- { label } + - { description &&
- { description } -
}
); } diff --git a/src/components/views/room_settings/AliasSettings.tsx b/src/components/views/room_settings/AliasSettings.tsx index 4666ca9ef7..bf847824d5 100644 --- a/src/components/views/room_settings/AliasSettings.tsx +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -28,6 +28,7 @@ import Modal from "../../../Modal"; import RoomPublishSetting from "./RoomPublishSetting"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import RoomAliasField from "../elements/RoomAliasField"; +import SettingsFieldset from "../settings/SettingsFieldset"; import { logger } from "matrix-js-sdk/src/logger"; @@ -380,6 +381,17 @@ export default class AliasSettings extends React.Component { return (
+ + { isSpaceRoom + ? _t("Published addresses can be used by anyone on any server to join your space.") + : _t("Published addresses can be used by anyone on any server to join your room.") } +   + { _t("To publish an address, it needs to be set as a local address first.") } + } + > + { /* { _t("Published Addresses") }

{ isSpaceRoom @@ -387,47 +399,46 @@ export default class AliasSettings extends React.Component { : _t("Published addresses can be used by anyone on any server to join your room.") }   { _t("To publish an address, it needs to be set as a local address first.") } -

- { canonicalAliasSection } - { this.props.hidePublishSetting - ? null - : } - - { this.getLocalNonAltAliases().map(alias => { - return - - - { _t("Local Addresses") } - -

- { isSpaceRoom +

*/ } + { canonicalAliasSection } + { this.props.hidePublishSetting + ? null + : } + + { this.getLocalNonAltAliases().map(alias => { + return + +
+ -
- { this.state.detailsOpen ? _t('Show less') : _t("Show more") } - { localAliasesList } -
+ "through your homeserver (%(localDomain)s)", { localDomain })}> +
+ { this.state.detailsOpen ? _t('Show less') : _t("Show more") } + { localAliasesList } +
+
+
); } diff --git a/src/components/views/room_settings/UrlPreviewSettings.tsx b/src/components/views/room_settings/UrlPreviewSettings.tsx index bb639b691a..2ce6680184 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.tsx +++ b/src/components/views/room_settings/UrlPreviewSettings.tsx @@ -27,6 +27,7 @@ import { SettingLevel } from "../../../settings/SettingLevel"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { Room } from "matrix-js-sdk/src/models/room"; import SettingsFlag from "../elements/SettingsFlag"; +import SettingsFieldset from '../settings/SettingsFieldset'; interface IProps { room: Room; @@ -96,18 +97,19 @@ export default class UrlPreviewSettings extends React.Component { roomId={roomId} /> ); - return ( -
-
- { _t('When someone puts a URL in their message, a URL preview can be shown to give more ' + + const description = <> +

+ { _t('When someone puts a URL in their message, a URL preview can be shown to give more ' + 'information about that link such as the title, description, and an image from the website.') } -

-
- { previewsForAccount } -
+

+

{ previewsForAccount }

+ ; + + return ( + { previewsForRoom } -
+ ); } } diff --git a/src/components/views/settings/SettingsFieldset.tsx b/src/components/views/settings/SettingsFieldset.tsx new file mode 100644 index 0000000000..431e65c4c7 --- /dev/null +++ b/src/components/views/settings/SettingsFieldset.tsx @@ -0,0 +1,31 @@ +/* +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 React, { ReactNode, HTMLAttributes } from 'react'; +import classNames from 'classnames'; + +interface Props extends HTMLAttributes { + // section title + legend: string | ReactNode; + description?: string | ReactNode; +} + +const SettingsFieldset: React.FC = ({ legend, className, children, description, ...rest }) => +
+ { legend } + { description &&
{ description }
} + { children } +
; + +export default SettingsFieldset; diff --git a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx index 1cfe023ba9..81526e597a 100644 --- a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx @@ -66,15 +66,9 @@ export default class GeneralRoomSettingsTab extends React.Component - { _t("URL Previews") } -
- -
- ; - if (!SettingsStore.getValue(UIFeature.URLPreviews)) { - urlPreviewSettings = null; - } + const urlPreviewSettings = SettingsStore.getValue(UIFeature.URLPreviews) ? + : + null; let flairSection; if (SettingsStore.getValue(UIFeature.Flair)) { @@ -110,14 +104,12 @@ export default class GeneralRoomSettingsTab extends React.Component
{ _t("Room Addresses") }
-
- -
+
{ _t("Other") }
{ flairSection } { urlPreviewSettings } diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx index 7a2b6f0d08..93b8a35bd4 100644 --- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx @@ -27,6 +27,7 @@ import { RoomState } from "matrix-js-sdk/src/models/room-state"; import { compare } from "../../../../../utils/strings"; import ErrorDialog from '../../../dialogs/ErrorDialog'; import PowerSelector from "../../../elements/PowerSelector"; +import SettingsFieldset from '../../SettingsFieldset'; import { logger } from "matrix-js-sdk/src/logger"; import SettingsStore from "../../../../../settings/SettingsStore"; @@ -345,17 +346,15 @@ export default class RolesRoomSettingsTab extends React.Component { if (privilegedUsers.length) { privilegedUsersSection = -
-
{ _t('Privileged Users') }
- { privilegedUsers } -
; + + { privilegedUsers } + ; } if (mutedUsers.length) { mutedUsersSection = -
-
{ _t('Muted Users') }
+ { mutedUsers } -
; + ; } } @@ -364,8 +363,7 @@ export default class RolesRoomSettingsTab extends React.Component { if (banned.length) { const canBanUsers = currentUserLevel >= banLevel; bannedUsersSection = -
-
{ _t('Banned users') }
+
    { banned.map((member) => { const banEvent = member.events.member.getContent(); @@ -383,7 +381,7 @@ export default class RolesRoomSettingsTab extends React.Component { ); }) }
-
; + ; } const powerSelectors = Object.keys(powerLevelDescriptors).map((key, index) => { @@ -452,15 +450,17 @@ export default class RolesRoomSettingsTab extends React.Component { { privilegedUsersSection } { mutedUsersSection } { bannedUsersSection } -
- { _t("Permissions") } -

{ isSpaceRoom - ? _t('Select the roles required to change various parts of the space') - : _t('Select the roles required to change various parts of the room') - }

+ { powerSelectors } { eventPowerSelectors } -
+ ); } diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index 0242ace0a6..32321ded9e 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -35,6 +35,7 @@ import createRoom, { IOpts } from '../../../../../createRoom'; import CreateRoomDialog from '../../../dialogs/CreateRoomDialog'; import JoinRuleSettings from "../../JoinRuleSettings"; import ErrorDialog from "../../../dialogs/ErrorDialog"; +import SettingsFieldset from '../../SettingsFieldset'; import { logger } from "matrix-js-sdk/src/logger"; @@ -265,14 +266,11 @@ export default class SecurityRoomSettingsTab extends React.Component ); } + const description = _t("Decide who can join %(roomName)s.", { + roomName: room?.name, + }); - return
-
- { _t("Decide who can join %(roomName)s.", { - roomName: room?.name, - }) } -
- + return { aliasWarning } -
; + ; } private onJoinRuleChangeError = (error: Error) => { @@ -330,6 +328,10 @@ export default class SecurityRoomSettingsTab extends React.Component -
- { _t('Changes to who can read history will only apply to future messages in this room. ' + - 'The visibility of existing history will be unchanged.') } -
- - - ); + const description = _t('Changes to who can read history will only apply to future messages in this room. ' + + 'The visibility of existing history will be unchanged.'); + + return ( + + ); } private toggleAdvancedSection = () => { @@ -416,15 +415,7 @@ export default class SecurityRoomSettingsTab extends React.Component; } - let historySection = (<> - { _t("Who can read history?") } -
- { this.renderHistory() } -
- ); - if (!SettingsStore.getValue(UIFeature.RoomHistorySettings)) { - historySection = null; - } + const historySection = this.renderHistory(); let advanced; if (room.getJoinRule() === JoinRule.Public) { @@ -446,26 +437,17 @@ export default class SecurityRoomSettingsTab extends React.Component
{ _t("Security & Privacy") }
- { _t("Encryption") } -
-
-
- { _t("Once enabled, encryption cannot be disabled.") } -
- -
+ + { encryptionSettings } -
+ - { _t("Access") } -
- { this.renderJoinRule() } -
+ { this.renderJoinRule() } { advanced } { historySection } diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index 5b06e1fdba..0ee9b48b76 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -98,15 +98,13 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space, closeSettingsFn if (space.getJoinRule() === JoinRule.Public) { addressesSection = <> { _t("Address") } -
- -
+ ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 15988c0353..20f0a2b546 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1495,7 +1495,6 @@ "This room is bridging messages to the following platforms. Learn more.": "This room is bridging messages to the following platforms. Learn more.", "This room isn't bridging messages to any platforms. Learn more.": "This room isn't bridging messages to any platforms. Learn more.", "Bridges": "Bridges", - "URL Previews": "URL Previews", "Room Addresses": "Room Addresses", "Uploaded sound": "Uploaded sound", "Get notifications as set up in your settings": "Get notifications as set up in your settings", @@ -1556,6 +1555,7 @@ "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.", "To link to this room, please add an address.": "To link to this room, please add an address.", "Decide who can join %(roomName)s.": "Decide who can join %(roomName)s.", + "Access": "Access", "Failed to update the join rules": "Failed to update the join rules", "Unknown failure": "Unknown failure", "Are you sure you want to make this encrypted room public?": "Are you sure you want to make this encrypted room public?", @@ -1566,12 +1566,11 @@ "Members only (since they joined)": "Members only (since they joined)", "Anyone": "Anyone", "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.", - "People with supported clients will be able to join the room without having a registered account.": "People with supported clients will be able to join the room without having a registered account.", "Who can read history?": "Who can read history?", + "People with supported clients will be able to join the room without having a registered account.": "People with supported clients will be able to join the room without having a registered account.", "Security & Privacy": "Security & Privacy", "Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.", "Encrypted": "Encrypted", - "Access": "Access", "Unable to revoke sharing for email address": "Unable to revoke sharing for email address", "Unable to share email address": "Unable to share email address", "Your email address hasn't been verified yet": "Your email address hasn't been verified yet", @@ -1881,6 +1880,7 @@ "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.", "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.", + "URL Previews": "URL Previews", "Back": "Back", "To proceed, please accept the verification request on your other login.": "To proceed, please accept the verification request on your other login.", "Waiting for %(displayName)s to accept…": "Waiting for %(displayName)s to accept…", diff --git a/test/components/views/settings/SettingsFieldset-test.tsx b/test/components/views/settings/SettingsFieldset-test.tsx new file mode 100644 index 0000000000..f056c02681 --- /dev/null +++ b/test/components/views/settings/SettingsFieldset-test.tsx @@ -0,0 +1,46 @@ +/* +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 React from 'react'; +import { renderIntoDocument } from 'react-dom/test-utils'; + +import SettingsFieldset from '../../../../src/components/views/settings/SettingsFieldset'; + +describe('', () => { + const defaultProps = { + "legend": 'Who can read history?', + "children":
test
, + 'data-test-id': 'test', + }; + const getComponent = (props = {}) => { + const wrapper = renderIntoDocument( +
, + ) as HTMLDivElement; + return wrapper.children[0]; + }; + + it('renders fieldset without description', () => { + expect(getComponent()).toMatchSnapshot(); + }); + + it('renders fieldset with plain text description', () => { + const description = 'Changes to who can read history.'; + expect(getComponent({ description })).toMatchSnapshot(); + }); + + it('renders fieldset with react description', () => { + const description = <>

Test

a link; + expect(getComponent({ description })).toMatchSnapshot(); + }); +}); diff --git a/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap new file mode 100644 index 0000000000..d5924073e3 --- /dev/null +++ b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders fieldset with plain text description 1`] = ` +
+ + Who can read history? + +
+ Changes to who can read history. +
+
+ test +
+
+`; + +exports[` renders fieldset with react description 1`] = ` +
+ + Who can read history? + +
+

+ Test +

+ + a link + +
+
+ test +
+
+`; + +exports[` renders fieldset without description 1`] = ` +
+ + Who can read history? + +
+ test +
+
+`;