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:
parent
0926c4c955
commit
db2b40543c
@ -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: {
|
||||||
|
@ -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({});
|
|
@ -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;
|
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
Loading…
Reference in New Issue
Block a user