1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

docs: remove unleash client from the docs (#3378)

We don't use the client for anything at the moment, so we can remove it.

While there are things we can use it for in the future, we should
instead add it back in when we get there.

This also removes the docs feedback form (which we haven't used since
the client stopped working)
This commit is contained in:
Thomas Heartman 2023-03-23 13:26:42 +01:00 committed by GitHub
parent 0926c4c955
commit db2b40543c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 0 additions and 867 deletions

View File

@ -53,9 +53,6 @@ module.exports = {
markdown: { mermaid: true }, markdown: { mermaid: true },
customFields: { customFields: {
// expose env vars etc here // expose env vars etc here
unleashProxyUrl: process.env.UNLEASH_PROXY_URL,
unleashProxyClientKey: process.env.UNLEASH_PROXY_CLIENT_KEY,
unleashFeedbackTargetUrl: process.env.UNLEASH_FEEDBACK_TARGET_URL,
environment: process.env.NODE_ENV, environment: process.env.NODE_ENV,
}, },
themeConfig: { themeConfig: {

View File

@ -1,48 +0,0 @@
import React from 'react';
import { initialData, FeedbackWrapper } from './index';
export default {
title: 'User feedback component',
component: FeedbackWrapper,
};
const Template = (args) => <FeedbackWrapper {...args} />;
export const Step1 = Template.bind({});
Step1.args = {
open: true,
seedData: {
currentStep: 1,
},
};
export const Step2 = Template.bind({});
Step2.args = {
seedData: {
currentStep: 2,
},
open: true,
};
export const Step3 = Template.bind({});
Step3.args = {
seedData: {
currentStep: 3,
},
open: true,
};
export const Step4 = Template.bind({});
Step4.args = {
seedData: {
currentStep: 4,
},
open: true,
};
export const WithLocalStorage = Template.bind({});
WithLocalStorage.args = {
open: true,
};
export const Closed = Template.bind({});

View File

@ -1,531 +0,0 @@
import React from 'react';
import styles from './styles.module.css';
import CloseIcon from '@site/src/icons/close';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
const join = (...cs: string[]) => cs.join(' ');
type CustomerType = 'open source' | 'paying';
type FormData = {
score: number;
comment: undefined | string;
customerType: undefined | CustomerType;
};
type InitialData = {
currentStep: number;
data: {
score: undefined | number;
comment: undefined | string;
customerType: undefined | CustomerType;
};
closedOrCompleted: boolean;
};
type CompleteData = InitialData & {
initialized: number;
};
const clearedData: InitialData = {
currentStep: 1,
data: {
score: undefined,
comment: undefined,
customerType: undefined,
},
closedOrCompleted: false,
};
const localstorageKey = 'user-feedback-v1';
const populateData = (initialData: InitialData): CompleteData => {
// if we get seed data, use that. Otherwise, check if the last entry in
// localstorage was completed. If not, use that as base.
const getSeedData = () => {
if (initialData) {
return initialData;
}
const userFeedbackLog = getUserDataRecord();
if (userFeedbackLog) {
const mostRecentTimestamp = Math.max(
...Object.keys(userFeedbackLog).map(parseInt),
);
const mostRecent = userFeedbackLog[mostRecentTimestamp];
if (mostRecent && !mostRecent.closedOrCompleted) {
return mostRecent;
}
}
return {};
};
const seedData = getSeedData();
return {
currentStep: 1,
...seedData,
data: {
score: undefined,
comment: undefined,
customerType: undefined,
...seedData?.data,
},
initialized: Date.now(),
};
};
const getUserDataRecord = () =>
JSON.parse(localStorage.getItem(localstorageKey));
const storeData = (data: CompleteData) => {
const existingData = getUserDataRecord();
localStorage.setItem(
localstorageKey,
JSON.stringify({
...existingData,
[data.initialized]: data,
}),
);
};
type Message =
| { kind: 'close' }
| { kind: 'completed' }
| { kind: 'reset' }
| { kind: 'set score'; data: number }
| { kind: 'set comment'; data: string }
| { kind: 'set customer type'; data: CustomerType }
| { kind: 'step forward' }
| { kind: 'step back' };
const stateReducer = (state: CompleteData, message: Message) => {
switch (message.kind) {
case 'close':
return { ...state, closedOrCompleted: true };
case 'completed':
return { ...state, closedOrCompleted: true };
case 'reset':
return { ...populateData(clearedData), closedOrCompleted: false };
case 'set score':
return {
...state,
data: { ...state.data, score: message.data },
};
case 'set comment':
return {
...state,
data: { ...state.data, comment: message.data },
};
case 'set customer type':
return {
...state,
data: { ...state.data, customerType: message.data },
};
case 'step forward':
return {
...state,
currentStep: Math.min(state.currentStep + 1, 4),
};
case 'step back':
return {
...state,
currentStep: Math.max(state.currentStep - 1, 1),
};
}
};
type Props = {
seedData?: InitialData;
open?: boolean;
};
export const FeedbackWrapper: React.FC<Props> = ({ seedData, open }) => {
const {
siteConfig: { customFields },
} = useDocusaurusContext();
const feedbackTargetUrl: string | undefined =
(customFields?.unleashFeedbackTargetUrl as string | undefined) ??
(typeof process !== 'undefined' &&
process?.env?.UNLEASH_FEEDBACK_TARGET_URL);
const [feedbackIsOpen, setFeedbackIsOpen] = React.useState(open);
const [manuallyOpened, setManuallyOpened] = React.useState(open);
const [state, dispatch] = React.useReducer(
stateReducer,
seedData,
populateData,
);
const close = () => dispatch({ kind: 'close' });
if (feedbackIsOpen) {
storeData(state);
}
const stepForward = () => {
dispatch({ kind: 'step forward' });
};
const stepBack = () => {
dispatch({ kind: 'step back' });
};
const setScore = (score: number) =>
dispatch({ kind: 'set score', data: score });
const setComment = (comment: string) =>
dispatch({ kind: 'set comment', data: comment });
const setCustomerType = (customerType: CustomerType) =>
dispatch({ kind: 'set customer type', data: customerType });
const submitFeedback = (data: FormData) => {
if (feedbackTargetUrl) {
fetch(feedbackTargetUrl, {
method: 'post',
body: JSON.stringify({
data: {
...data,
openedManually: manuallyOpened,
currentPage: location.pathname,
},
}),
headers: {
'content-type': 'application/json',
},
})
.then(async (res) =>
res.ok
? console.log('Success! Feedback was registered.')
: console.warn(
`Oh, no! The feedback registration failed: ${await res.text()}`,
),
)
.catch((e) =>
console.error(
'Oh, no! The feedback registration failed:',
e,
),
);
} else {
console.warn(
'No target url specified for feedback. Not doing anything.',
);
}
dispatch({ kind: 'completed' });
stepForward();
};
const visuallyHidden = (stepNumber: number) =>
state.currentStep !== stepNumber;
const isHidden = (stepNumber: number) =>
!feedbackIsOpen || visuallyHidden(stepNumber);
const Step1 = () => {
const hidden = isHidden(1);
const [newValue, setNewValue] = React.useState(state.data.score);
return (
<form
className={visuallyHidden(1) ? styles['invisible'] : ''}
onSubmit={(e) => {
e.preventDefault();
setScore(newValue);
stepForward();
}}
aria-hidden={hidden}
>
<fieldset disabled={hidden}>
<p>
<span className="visually-hidden">
On a scale from 1 to 5 where 1 is very unsatisfied
and 5 is very satisfied,
</span>{' '}
How would you rate your overall satisfaction with the
Unleash documentation?
</p>
<div className={styles['satisfaction-input-container']}>
<span
aria-hidden="true"
className={
styles['satisfaction-input-visual-label']
}
>
Very unsatisfied
</span>
<span className={styles['satisfaction-input-inputs']}>
{[1, 2, 3, 4, 5].map((n, i) => (
<span key={`input-group-${n}`}>
<input
className={join(
'visually-hidden',
styles[
'user-satisfaction-score-input'
],
)}
required
id={`user-satisfaction-score-${n}`}
name="satisfaction-level"
type="radio"
value={n}
defaultChecked={n === state.data.score}
onChange={(e) => {
const value = parseInt(
e.target.value,
);
setNewValue(value);
}}
autoFocus={
manuallyOpened
? state.data.score
? state.data.score === n
: i === 0
: false
}
/>
<label
className={
styles[
'user-satisfaction-score-label'
]
}
htmlFor={`user-satisfaction-score-${n}`}
>
{n}
</label>
</span>
))}
</span>
<span
aria-hidden="true"
className={
styles['satisfaction-input-visual-label']
}
>
Very satisfied
</span>
</div>
<div className={styles['button-container']}>
<button type="submit">Next</button>
</div>
</fieldset>
</form>
);
};
const Step2 = () => {
const hidden = isHidden(2);
const textareaId = 'feedback-comment-input';
const saveComment = () =>
setComment(
(document.getElementById(textareaId) as HTMLTextAreaElement)
.value,
);
return (
<form
className={visuallyHidden(2) ? styles['invisible'] : ''}
aria-hidden={hidden}
onSubmit={(e) => {
e.preventDefault();
saveComment();
stepForward();
}}
>
<fieldset disabled={hidden}>
<label htmlFor={textareaId}>
What would you like to see improved in the Unleash
documentation?
</label>
<textarea
id={textareaId}
name=""
rows={3}
autoFocus
defaultValue={state.data.comment}
></textarea>
<div className={styles['button-container']}>
<button type="submit">Next</button>
<button
className={styles['button-secondary']}
type="button"
onClick={() => {
saveComment();
stepForward();
}}
>
Skip
</button>
<button
className={styles['button-secondary']}
type="button"
onClick={() => {
saveComment();
stepBack();
}}
>
Back
</button>
</div>
</fieldset>
</form>
);
};
const Step3 = () => {
const hidden = isHidden(3);
const [value, setValue] = React.useState<CustomerType | undefined>(
state.data.customerType,
);
return (
<form
className={visuallyHidden(3) ? styles['invisible'] : ''}
aria-hidden={hidden}
onSubmit={(e) => {
e.preventDefault();
setCustomerType(value);
// To ensure that we get the correct customer type included.
// We can't rely on the reducer to set it because it won't
// happen until the component re-renders, causing customer
// type to have an old or empty value.
const finalState = stateReducer(state, {
kind: 'set customer type',
data: value,
});
submitFeedback(finalState.data);
}}
>
<fieldset disabled={hidden}>
<span>
Finally, are you a paying customer or an open source
customer of Unleash?
</span>
<div className={styles['customer-type-inputs']}>
{[
['a', 'paying', 'paying'],
['an', 'open source', 'opensource'],
].map(([article, customerType, key], i) => (
<span key={`input-group-${key}`}>
<input
autoFocus={
state.data.customerType
? state.data.customerType === key
: i === 0
}
id={`customer-type-${key}`}
className={styles['customer-type-input']}
name="customer-type"
type="radio"
value={key}
defaultChecked={
customerType === state.data.customerType
}
onChange={() => {
setValue(customerType as CustomerType);
}}
/>
<label
className={styles['customer-type-label']}
htmlFor={`customer-type-${key}`}
>
{`I'm ${article} ${customerType} customer`}
</label>
</span>
))}
</div>
<div className={styles['button-container']}>
<button type="submit">Submit</button>
<button
className={styles['button-secondary']}
type="button"
onClick={() => {
setCustomerType(value);
stepBack();
}}
>
Back
</button>
</div>
</fieldset>
</form>
);
};
const Step4 = () => {
const hidden = isHidden(4);
return (
<div className={visuallyHidden(4) ? styles['invisible'] : ''}>
<p className={styles['thank-you']}>Thank you! 🙌</p>
<button
className={styles['button-secondary']}
disabled={hidden}
onClick={() => {
setFeedbackIsOpen(false);
close();
}}
autoFocus
>
close
</button>
</div>
);
};
return (
<div className={styles['user-feedback-container']}>
<button
aria-hidden={feedbackIsOpen}
className={join(
styles['open-feedback-button'],
styles['primary-button'],
)}
disabled={feedbackIsOpen}
onClick={() => {
setFeedbackIsOpen(true);
setManuallyOpened(true);
if (state.closedOrCompleted) {
dispatch({ kind: 'reset' });
}
}}
>
<span>Feedback</span>
</button>
<article
aria-hidden={!feedbackIsOpen}
className={join(
styles['user-feedback'],
feedbackIsOpen ? '' : styles['invisible'],
)}
>
<div className={styles['close-button-row']}>
<button
onClick={() => {
setFeedbackIsOpen(false);
close();
}}
className={styles['close-button']}
disabled={!feedbackIsOpen}
>
<span className="visually-hidden">
close feedback popup
</span>
<CloseIcon />
</button>
</div>
<div className={styles['form-section-container']}>
<Step1 />
<Step2 />
<Step3 />
<Step4 />
</div>
</article>
</div>
);
};
export default FeedbackWrapper;

View File

@ -1,244 +0,0 @@
.user-feedback-container {
--outline-style: 2px solid var(--ifm-color-primary);
--row-gap: 1rem;
--element-horizontal-gap: 1rem;
--animation-duration: 0.25s;
--fade-out-transition: opacity var(--animation-duration);
--fade-in-transition: opacity var(--animation-duration)
calc(var(--animation-duration) / 2);
}
@media screen and (prefers-reduced-motion: reduced) {
.user-feedback-container {
--animation-duration: 0;
}
}
.user-feedback {
width: 100%;
position: fixed;
background: var(--ifm-background-color);
bottom: 0;
border: var(--ifm-global-border-width) solid var(--unleash-color-gray);
border-radius: var(--ifm-global-radius) var(--ifm-global-radius) 0 0;
box-shadow: var(--ifm-global-shadow-lw);
padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal);
text-align: center;
transition: var(--fade-in-transition);
}
.user-feedback fieldset {
border: none;
margin: 0;
padding: 0;
width: 100%;
}
:is(.user-feedback, .user-feedback fieldset) > * + * {
margin-top: var(--row-gap);
}
.user-feedback button {
border: none;
border-radius: var(--ifm-global-radius);
padding: var(--ifm-spacing-vertical) calc(var(--ifm-spacing-horizontal) / 2);
}
.user-feedback form > * + * {
margin-top: var(--row-gap);
}
.hidden {
display: none;
}
.user-feedback-container * {
outline-offset: 4px;
}
.user-feedback-container *:focus-visible {
outline: var(--outline-style);
}
.satisfaction-input-container {
display: flex;
flex-flow: wrap;
place-content: center;
align-items: center;
gap: var(--element-horizontal-gap);
}
.satisfaction-input-inputs {
display: flex;
flex-flow: wrap;
place-content: center;
align-items: center;
gap: var(--element-horizontal-gap);
}
.satisfaction-input-visual-label {
display: none;
}
@media screen and (min-width: 800px) {
.satisfaction-input-visual-label {
display: inline;
}
}
@media screen and (max-width: 400px) {
.satisfaction-input-inputs {
gap: calc(var(--element-horizontal-gap) / 2);
}
}
.user-satisfaction-score-label {
display: grid;
place-content: center;
height: 3em;
width: 3em;
border: var(--ifm-global-border-width) solid currentColor;
border-radius: 50%;
}
.user-satisfaction-score-input:focus-visible + .user-satisfaction-score-label {
outline: var(--outline-style);
}
.user-satisfaction-score-label:hover {
color: var(--ifm-color-primary);
}
.user-satisfaction-score-input:checked + label {
color: var(--ifm-color-primary);
background: var(--ifm-color-primary);
color: var(--ifm-color-primary-contrast-background);
border-color: var(--ifm-color-primary);
}
.button-container {
margin-top: var(--row-gap);
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
gap: var(--element-horizontal-gap);
}
button.close-button {
background: none;
border: none;
border-radius: 50%;
padding: 0;
aspect-ratio: 1;
height: 1em;
color: var(--ifm-font-color-base);
}
.close-button:hover {
color: var(--ifm-color-primary);
}
.close-button:active {
color: var(--ifm-color-primary-darker);
}
.close-button-row {
display: flex;
justify-content: flex-end;
}
.close-button svg {
fill: currentColor;
}
.primary-button,
.user-feedback button[type='submit'] {
background-color: var(--ifm-color-primary);
color: var(--ifm-background-color);
padding-inline: calc(var(--ifm-spacing-horizontal) * 4);
}
.primary-button:hover,
.user-feedback button[type='submit']:hover {
background-color: var(--ifm-color-primary-lighter);
}
.primary-button:hover,
.user-feedback button[type='submit']:active {
background-color: var(--ifm-color-primary-dark);
}
.button-secondary {
color: var(--ifm-color-primary);
background: none;
}
.button-secondary:active {
color: var(--ifm-color-primary-darker);
}
.button-secondary:hover {
color: var(--ifm-color-primary-lightest);
}
.user-feedback textarea {
display: block;
width: 100%;
background-color: var(--ifm-background-color);
color: currentColor;
border-radius: var(--ifm-global-radius);
border: var(--ifm-global-border-width) solid var(--ifm-color-emphasis-400);
font-style: normal;
font-family: inherit;
padding: var(--ifm-spacing-vertical) var(--ifm-spacing-horizontal);
}
.customer-type-inputs {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: var(--ifm-spacing-horizontal);
accent-color: var(--ifm-color-primary);
}
.open-feedback-button {
padding-block: var(--ifm-spacing-vertical);
padding-inline: var(--ifm-spacing-horizontal);
border-radius: 0 var(--ifm-global-radius) var(--ifm-global-radius) 0;
border: none;
position: fixed;
bottom: 25vh;
right: 0;
transition: var(--fade-in-transition);
transform: rotate(180deg);
writing-mode: vertical-lr;
}
/* note: Chrome doesn't support writing-mode on buttons, so we need to add a
span for the text and change the writing-mode there. Simultaneously, Firefox
does some weird stuff with the padding of the text if writing-mode isn't
specified on the button itself, so we need to set that too. */
.open-feedback-button > span {
writing-mode: vertical-lr;
}
.invisible,
.open-feedback-button[disabled] {
opacity: 0;
transition: var(--fade-out-transition);
pointer-events: none;
}
.form-section-container {
display: grid;
align-items: center;
max-width: 850px;
margin: auto;
}
.form-section-container > * {
grid-column: 1;
grid-row: 1;
transition: var(--fade-in-transition);
}

View File

@ -1,41 +0,0 @@
import React from 'react';
import UserFeedback from '@site/src/components/UserFeedback';
import { UnleashClient } from 'unleash-proxy-client';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
// Default implementation, that you can customize
function Root({ children }) {
const {
siteConfig: { customFields },
} = useDocusaurusContext();
const unleashConfig = {
clientKey: customFields.unleashProxyClientKey as string,
url: customFields.unleashProxyUrl as string,
disableRefresh: true,
appName: `docs.getunleash.io-${customFields.environment}`,
};
const [showFeedback, setShowFeedback] = React.useState(false);
if (typeof fetch !== 'undefined') {
try {
const unleash = new UnleashClient(unleashConfig);
unleash.on('ready', () => {
setShowFeedback(unleash.isEnabled('docs-feedback-survey-v1'));
});
unleash.start();
} catch (e) {
console.warn('Unable to initialize the Unleash client:', e.message);
}
}
return (
<>
{children}
{showFeedback && <UserFeedback />}
</>
);
}
export default Root;