Skip to content

Commit

Permalink
🎨 Made 5 settings quicker to edit at the top-level in Settings (#21976)
Browse files Browse the repository at this point in the history
Based on our changes to the _Access_ and _Analytics_ cards in Settings,
we decided to update how we allow edits to a few other settings, too.

These changes allow the following settings to be manipulated at the
top-level in Settings, without having to click 'Edit' first.

- Timezone
- Default recipients for newsletters
- Publication language
- Social accounts
- Tips and donations

fixes
https://linear.app/ghost/issue/DES-1062/updates-to-editsave-method-of-settings-cards
  • Loading branch information
dvdwinden authored Jan 8, 2025
1 parent d9abbb5 commit 171036e
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 354 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, {useState} from 'react';
import React, {useEffect, useState} from 'react';
import TopLevelGroup from '../../TopLevelGroup';
import useDefaultRecipientsOptions from './useDefaultRecipientsOptions';
import useSettingGroup from '../../../hooks/useSettingGroup';
import {MultiSelect, MultiSelectOption, Select, SettingGroupContent, withErrorBoundary} from '@tryghost/admin-x-design-system';
import {MultiValue} from 'react-select';
import {getOptionLabel} from '../../../utils/helpers';
import {getSettingValues} from '@tryghost/admin-x-framework/api/settings';

type RefipientValueArgs = {
Expand Down Expand Up @@ -75,6 +74,15 @@ const DefaultRecipients: React.FC<{ keywords: string[] }> = ({keywords}) => {

const {loadOptions, selectedSegments, setSelectedSegments} = useDefaultRecipientsOptions(selectedOption, defaultEmailRecipientsFilter);

// Update local state when settings change (e.g., after cancel)
useEffect(() => {
const newValue = getDefaultRecipientValue({
defaultEmailRecipients,
defaultEmailRecipientsFilter
});
setSelectedOption(newValue);
}, [defaultEmailRecipients, defaultEmailRecipientsFilter]);

const setDefaultRecipientValue = (value: string) => {
if (['visibility', 'disabled'].includes(value)) {
updateSetting('editor_default_email_recipients', value);
Expand All @@ -96,6 +104,9 @@ const DefaultRecipients: React.FC<{ keywords: string[] }> = ({keywords}) => {
}

setSelectedOption(value);
if (!isEditing) {
handleEditingChange(true);
}
};

const updateSelectedSegments = (selected: MultiValue<MultiSelectOption>) => {
Expand All @@ -108,47 +119,11 @@ const DefaultRecipients: React.FC<{ keywords: string[] }> = ({keywords}) => {
updateSetting('editor_default_email_recipients_filter', null);
setSelectedOption('none');
}
if (!isEditing) {
handleEditingChange(true);
}
};

const values = (
<SettingGroupContent
values={[
{
heading: 'Default Newsletter recipients',
key: 'default-recipients',
value: getOptionLabel(RECIPIENT_FILTER_OPTIONS, selectedOption)
}
]}
/>
);

const form = (
<SettingGroupContent columns={1}>
<Select
hint='Who should receive your posts by default?'
options={RECIPIENT_FILTER_OPTIONS}
selectedOption={RECIPIENT_FILTER_OPTIONS.find(option => option.value === selectedOption)}
testId='default-recipients-select'
title="Default Newsletter recipients"
onSelect={(option) => {
if (option) {
setDefaultRecipientValue(option.value);
}
}}
/>
{(selectedOption === 'segment') && selectedSegments && (
<MultiSelect
loadOptions={loadOptions}
title='Filter'
values={selectedSegments}
async
defaultOptions
onChange={updateSelectedSegments}
/>
)}
</SettingGroupContent>
);

return (
<TopLevelGroup
description='When you publish new content, who do you usually want to send it to?'
Expand All @@ -158,11 +133,35 @@ const DefaultRecipients: React.FC<{ keywords: string[] }> = ({keywords}) => {
saveState={saveState}
testId='default-recipients'
title='Default recipients'
hideEditButton
onCancel={handleCancel}
onEditingChange={handleEditingChange}
onSave={handleSave}
>
{isEditing ? form : values}
<SettingGroupContent columns={1}>
<Select
hint='Who should receive your posts by default?'
options={RECIPIENT_FILTER_OPTIONS}
selectedOption={RECIPIENT_FILTER_OPTIONS.find(option => option.value === selectedOption)}
testId='default-recipients-select'
title="Default Newsletter recipients"
onSelect={(option) => {
if (option) {
setDefaultRecipientValue(option.value);
}
}}
/>
{(selectedOption === 'segment') && selectedSegments && (
<MultiSelect
loadOptions={loadOptions}
title='Filter'
values={selectedSegments}
async
defaultOptions
onChange={updateSelectedSegments}
/>
)}
</SettingGroupContent>
</TopLevelGroup>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const PublicationLanguage: React.FC<{ keywords: string[] }> = ({keywords}) => {
handleSave,
handleCancel,
updateSetting,
focusRef,
errors,
clearError,
handleEditingChange
Expand All @@ -32,40 +31,18 @@ const PublicationLanguage: React.FC<{ keywords: string[] }> = ({keywords}) => {

const handleLanguageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
updateSetting('locale', e.target.value);
if (!isEditing) {
handleEditingChange(true);
}
};

const values = (
<SettingGroupContent values={[
{
heading: 'Site language',
key: 'site-language',
value: publicationLanguage
}
]} />
);

const hint = (
<>
Default: English (<strong>en</strong>); find out more about
<a className='text-green-400' href="https://ghost.org/docs/faq/translation/" rel="noopener noreferrer" target="_blank"> using Ghost in other languages</a>
</>
);

const inputFields = (
<SettingGroupContent columns={1}>
<TextField
error={!!errors.publicationLanguage}
hint={errors.publicationLanguage || hint}
inputRef={focusRef}
placeholder="Site language"
title='Site language'
value={publicationLanguage}
onChange={handleLanguageChange}
onKeyDown={() => clearError('password')}
/>
</SettingGroupContent>
);

return (
<TopLevelGroup
description="Set the language/locale which is used on your site"
Expand All @@ -75,11 +52,22 @@ const PublicationLanguage: React.FC<{ keywords: string[] }> = ({keywords}) => {
saveState={saveState}
testId='publication-language'
title="Publication Language"
hideEditButton
onCancel={handleCancel}
onEditingChange={handleEditingChange}
onSave={handleSave}
>
{isEditing ? inputFields : values}
<SettingGroupContent columns={1}>
<TextField
error={!!errors.publicationLanguage}
hint={errors.publicationLanguage || hint}
placeholder="Site language"
title='Site language'
value={publicationLanguage}
onChange={handleLanguageChange}
onKeyDown={() => clearError('password')}
/>
</SettingGroupContent>
</TopLevelGroup>
);
};
Expand Down
Loading

0 comments on commit 171036e

Please sign in to comment.