mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: reset editor layout button (#57104)
This commit is contained in:
@@ -238,6 +238,8 @@
|
||||
"sound-mode": "This adds the pleasant sound of acoustic guitar throughout the website. You'll get musical feedback as you type in the editor, complete challenges, claim certifications, and more.",
|
||||
"sound-volume": "Campfire Volume:",
|
||||
"scrollbar-width": "Editor Scrollbar Width",
|
||||
"reset-editor-layout-tooltip": "Reset the editor layout to its default state",
|
||||
"reset-editor-layout": "Reset Editor Layout",
|
||||
"shortcuts-explained": "Within a challenge, press ESC followed by the question mark to show a list of available shortcuts.",
|
||||
"username": {
|
||||
"contains invalid characters": "Username \"{{username}}\" contains invalid characters",
|
||||
@@ -896,6 +898,7 @@
|
||||
"invalid-update-flag": "You are attempting to access forbidden resources. Please request assistance on https://forum.freecodecamp.org if this is a valid request.",
|
||||
"generate-exam-error": "An error occurred trying to generate your exam.",
|
||||
"cert-not-found": "The certification {{certSlug}} does not exist.",
|
||||
"reset-editor-layout": "Your editor layout has been reset.",
|
||||
"ms": {
|
||||
"transcript": {
|
||||
"link-err-1": "Please include a Microsoft transcript URL in the request.",
|
||||
|
||||
@@ -35,7 +35,8 @@ import {
|
||||
updateMySound,
|
||||
updateMyTheme,
|
||||
updateMyKeyboardShortcuts,
|
||||
verifyCert
|
||||
verifyCert,
|
||||
resetMyEditorLayout
|
||||
} from '../redux/settings/actions';
|
||||
|
||||
const { apiLocation } = envData;
|
||||
@@ -47,6 +48,7 @@ type ShowSettingsProps = Pick<ThemeProps, 'toggleNightMode'> & {
|
||||
navigate: (location: string) => void;
|
||||
showLoading: boolean;
|
||||
toggleSoundMode: (sound: boolean) => void;
|
||||
resetEditorLayout: () => void;
|
||||
toggleKeyboardShortcuts: (keyboardShortcuts: boolean) => void;
|
||||
updateIsHonest: () => void;
|
||||
updateQuincyEmail: (isSendQuincyEmail: boolean) => void;
|
||||
@@ -80,6 +82,7 @@ const mapDispatchToProps = {
|
||||
updateIsHonest: updateMyHonesty,
|
||||
updateQuincyEmail: (sendQuincyEmail: boolean) =>
|
||||
updateMyQuincyEmail({ sendQuincyEmail }),
|
||||
resetEditorLayout: () => resetMyEditorLayout(),
|
||||
verifyCert
|
||||
};
|
||||
|
||||
@@ -91,6 +94,7 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
|
||||
toggleNightMode,
|
||||
toggleSoundMode,
|
||||
toggleKeyboardShortcuts,
|
||||
resetEditorLayout,
|
||||
user: {
|
||||
completedChallenges,
|
||||
email,
|
||||
@@ -140,6 +144,7 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
|
||||
return <Loader fullScreen={true} />;
|
||||
}
|
||||
const sound = (store.get('fcc-sound') as boolean) ?? false;
|
||||
const editorLayout = (store.get('challenge-layout') as boolean) ?? false;
|
||||
return (
|
||||
<>
|
||||
<Helmet title={`${t('buttons.settings')} | freeCodeCamp.org`} />
|
||||
@@ -161,6 +166,8 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
|
||||
currentTheme={theme}
|
||||
keyboardShortcuts={keyboardShortcuts}
|
||||
sound={sound}
|
||||
editorLayout={editorLayout}
|
||||
resetEditorLayout={resetEditorLayout}
|
||||
toggleKeyboardShortcuts={toggleKeyboardShortcuts}
|
||||
toggleNightMode={toggleNightMode}
|
||||
toggleSoundMode={toggleSoundMode}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Spacer } from '@freecodecamp/ui';
|
||||
import { Button, Spacer } from '@freecodecamp/ui';
|
||||
import { FullWidthRow } from '../helpers';
|
||||
import ThemeSettings, { ThemeProps } from '../../components/settings/theme';
|
||||
import SoundSettings from '../../components/settings/sound';
|
||||
@@ -11,15 +11,19 @@ type MiscSettingsProps = ThemeProps & {
|
||||
currentTheme: string;
|
||||
keyboardShortcuts: boolean;
|
||||
sound: boolean;
|
||||
editorLayout: boolean | null;
|
||||
toggleKeyboardShortcuts: (keyboardShortcuts: boolean) => void;
|
||||
toggleNightMode: () => void;
|
||||
toggleSoundMode: (sound: boolean) => void;
|
||||
resetEditorLayout: () => void;
|
||||
};
|
||||
|
||||
const MiscSettings = ({
|
||||
currentTheme,
|
||||
keyboardShortcuts,
|
||||
sound,
|
||||
editorLayout,
|
||||
resetEditorLayout,
|
||||
toggleKeyboardShortcuts,
|
||||
toggleNightMode,
|
||||
toggleSoundMode
|
||||
@@ -41,6 +45,19 @@ const MiscSettings = ({
|
||||
explain={t('settings.shortcuts-explained')?.toString()}
|
||||
/>
|
||||
<ScrollbarWidthSettings />
|
||||
<label htmlFor='reset-layout-btn'>
|
||||
{t('settings.reset-editor-layout-tooltip')}
|
||||
</label>
|
||||
<Spacer size='xs' />
|
||||
<Button
|
||||
onClick={resetEditorLayout}
|
||||
id='reset-layout-btn'
|
||||
data-playwright-test-label='reset-layout-btn'
|
||||
disabled={!editorLayout}
|
||||
aria-disabled={!editorLayout}
|
||||
>
|
||||
{t('settings.reset-editor-layout')}
|
||||
</Button>
|
||||
</FullWidthRow>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -491,6 +491,8 @@ export const reducer = handleActions(
|
||||
payload ? spreadThePayloadOnUser(state, payload) : state,
|
||||
[settingsTypes.updateMyPortfolioComplete]: (state, { payload }) =>
|
||||
payload ? spreadThePayloadOnUser(state, payload) : state,
|
||||
[settingsTypes.resetMyEditorLayoutComplete]: (state, { payload }) =>
|
||||
payload ? spreadThePayloadOnUser(state, payload) : state,
|
||||
[settingsTypes.verifyCertComplete]: (state, { payload }) =>
|
||||
payload ? spreadThePayloadOnUser(state, payload) : state,
|
||||
[settingsTypes.submitProfileUIComplete]: (state, { payload }) =>
|
||||
|
||||
@@ -18,6 +18,7 @@ export const actionTypes = createTypes(
|
||||
...createAsyncTypes('submitProfileUI'),
|
||||
...createAsyncTypes('verifyCert'),
|
||||
...createAsyncTypes('resetProgress'),
|
||||
...createAsyncTypes('resetMyEditorLayout'),
|
||||
...createAsyncTypes('deleteAccount')
|
||||
],
|
||||
ns
|
||||
|
||||
@@ -105,5 +105,13 @@ export const verifyCertError = createAction(types.verifyCertError);
|
||||
export const resetProgress = createAction(types.resetProgress);
|
||||
export const resetProgressError = createAction(types.resetProgressError);
|
||||
|
||||
export const resetMyEditorLayout = createAction(types.resetMyEditorLayout);
|
||||
export const resetMyEditorLayoutComplete = createAction(
|
||||
types.resetMyEditorLayoutComplete
|
||||
);
|
||||
export const resetMyEditorLayoutError = createAction(
|
||||
types.resetMyEditorLayoutError
|
||||
);
|
||||
|
||||
export const deleteAccount = createAction(types.deleteAccount);
|
||||
export const deleteAccountError = createAction(types.deleteAccountError);
|
||||
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
} from '../../utils/ajax';
|
||||
import { completedChallengesSelector } from '../selectors';
|
||||
import {
|
||||
resetMyEditorLayoutComplete,
|
||||
resetMyEditorLayoutError,
|
||||
submitNewAboutComplete,
|
||||
submitNewAboutError,
|
||||
submitNewUsernameComplete,
|
||||
@@ -110,6 +112,26 @@ function* updateMySoundSaga({ payload: update }) {
|
||||
}
|
||||
}
|
||||
|
||||
function* resetMyEditorLayoutSaga() {
|
||||
const layout = store.get('challenge-layout');
|
||||
|
||||
if (layout) {
|
||||
try {
|
||||
const data = {
|
||||
message: 'flash.reset-editor-layout',
|
||||
type: 'success'
|
||||
};
|
||||
|
||||
store.remove('challenge-layout');
|
||||
|
||||
yield put(createFlashMessage({ ...data }));
|
||||
yield put(resetMyEditorLayoutComplete({ ...data }));
|
||||
} catch (e) {
|
||||
yield put(resetMyEditorLayoutError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function* updateMyThemeSaga({ payload: update }) {
|
||||
try {
|
||||
const { data } = yield call(putUpdateMyTheme, update);
|
||||
@@ -225,6 +247,7 @@ export function createSettingsSagas(types) {
|
||||
takeEvery(types.updateMySocials, updateMySocialsSaga),
|
||||
takeEvery(types.updateMyHonesty, updateMyHonestySaga),
|
||||
takeEvery(types.updateMySound, updateMySoundSaga),
|
||||
takeEvery(types.resetMyEditorLayout, resetMyEditorLayoutSaga),
|
||||
takeEvery(types.updateMyTheme, updateMyThemeSaga),
|
||||
takeEvery(types.updateMyKeyboardShortcuts, updateMyKeyboardShortcutsSaga),
|
||||
takeEvery(types.updateMyQuincyEmail, updateMyQuincyEmailSaga),
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
test.use({ storageState: 'playwright/.auth/certified-user.json' });
|
||||
test.describe('Reset Editor Layout', () => {
|
||||
test('drag layout and reset', async ({ page, isMobile }) => {
|
||||
test.skip(
|
||||
isMobile,
|
||||
'The mobile layout does not have resizable panes, so this test is not applicable.'
|
||||
);
|
||||
|
||||
await page.goto(
|
||||
'/learn/2022/responsive-web-design/learn-basic-css-by-building-a-cafe-menu/step-15'
|
||||
);
|
||||
|
||||
const desktopLayout = page.getByTestId('desktop-layout');
|
||||
const splitter = page.getByTestId('preview-left-splitter');
|
||||
const editorPane = desktopLayout.getByTestId('editor-pane');
|
||||
const initialStyle = await editorPane.getAttribute('style');
|
||||
|
||||
expect(initialStyle).toContain('flex: 1');
|
||||
|
||||
// Drag the splitter to resize the editor pane
|
||||
await splitter.hover();
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(100, 100);
|
||||
await page.mouse.up();
|
||||
|
||||
const newStyle = await editorPane.getAttribute('style');
|
||||
expect(newStyle).not.toContain('flex: 1');
|
||||
|
||||
await page.goto('/settings#privacy-settings');
|
||||
|
||||
const resetButton = page.getByTestId('reset-layout-btn');
|
||||
await resetButton.click();
|
||||
|
||||
await expect(page.getByTestId('flash-message')).toContainText(
|
||||
'Your editor layout has been reset'
|
||||
);
|
||||
|
||||
await expect(resetButton).toBeDisabled();
|
||||
|
||||
await page.goto(
|
||||
'/learn/2022/responsive-web-design/learn-basic-css-by-building-a-cafe-menu/step-15'
|
||||
);
|
||||
|
||||
const afterReset = await editorPane.getAttribute('style');
|
||||
expect(afterReset).toContain('flex: 1');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user