Skip to content

Commit 2daf36b

Browse files
committed
fixup basic styles and override options
1 parent bafe626 commit 2daf36b

File tree

10 files changed

+145
-125
lines changed

10 files changed

+145
-125
lines changed

packages/feedback2/src/core/components/Actor.css.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,26 @@ export function createActorStyles(): HTMLStyleElement {
77
const style = DOCUMENT.createElement('style');
88
style.textContent = `
99
.widget__actor {
10+
position: fixed;
11+
left: var(--left);
12+
right: var(--right);
13+
bottom: var(--bottom);
14+
top: var(--top);
15+
z-index: var(--z-index);
16+
1017
line-height: 25px;
1118
1219
display: flex;
1320
align-items: center;
1421
gap: 8px;
22+
z-index: 9000;
1523
1624
border-radius: var(--border-radius);
1725
cursor: pointer;
1826
font-size: 14px;
1927
font-weight: 600;
2028
padding: 12px 16px;
2129
text-decoration: none;
22-
z-index: 9000;
2330
2431
color: var(--foreground);
2532
background-color: var(--background);

packages/feedback2/src/core/createMainStyles.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,13 @@ export function createMainStyles(colorScheme: 'system' | 'dark' | 'light', theme
4444
style.textContent = `
4545
:host {
4646
--bottom: 1rem;
47-
--right: 1rem;
47+
--right: 1rem; /* this is the font-size of the page, not the shadowroot */
4848
--top: auto;
4949
--left: auto;
5050
--z-index: 100000;
5151
--font-family: ${themes.themeLight.fontFamily};
5252
--font-size: ${themes.themeLight.fontSize};
5353
54-
position: fixed;
55-
left: var(--left);
56-
right: var(--right);
57-
bottom: var(--bottom);
58-
top: var(--top);
59-
z-index: var(--z-index);
60-
6154
font-family: var(--font-family);
6255
font-size: var(--font-size);
6356

packages/feedback2/src/core/integration.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ import {
1919
import type { DialogComponent } from '../modal/components/Dialog';
2020
import type { feedback2ModalIntegration } from '../modal/integration';
2121
import type { feedback2ScreenshotIntegration } from '../screenshot/integration';
22-
import type { FeedbackCallbacks, FeedbackInternalOptions, OptionalFeedbackConfiguration } from '../types';
22+
import type { FeedbackInternalOptions, OptionalFeedbackConfiguration, OverrideFeedbackConfiguration } from '../types';
2323
import { DEBUG_BUILD } from '../util/debug-build';
24-
import { mergeOptions } from '../util/mergeOptions';
2524
import { Actor } from './components/Actor';
2625
import { createMainStyles } from './createMainStyles';
2726
import { sendFeedback } from './sendFeedback';
@@ -163,7 +162,6 @@ export class Feedback2 implements Integration {
163162

164163
this._host = null;
165164
this._shadow = null;
166-
167165
this._dialog = null;
168166
}
169167

@@ -213,19 +211,19 @@ export class Feedback2 implements Integration {
213211
}
214212

215213
/**
216-
* Adds click listener to attached element to open a feedback dialog
214+
* Adds click listener to the element to open a feedback dialog
217215
*
218-
* The returned function can be used to detact the click listener
216+
* The returned function can be used to remove the click listener
219217
*/
220-
public attachTo(el: Element | string, optionOverrides: Partial<FeedbackCallbacks> = {}): null | (() => void) {
221-
const options = mergeOptions(this.options, optionOverrides);
218+
public attachTo(el: Element | string, optionOverrides: OverrideFeedbackConfiguration = {}): () => void {
219+
const options = { ...this.options, ...optionOverrides };
222220

223221
const targetEl =
224222
typeof el === 'string' ? DOCUMENT.querySelector(el) : typeof el.addEventListener === 'function' ? el : null;
225223

226224
if (!targetEl) {
227225
DEBUG_BUILD && logger.error('[Feedback] Unable to attach to target element');
228-
return null;
226+
throw new Error('Unable to attach to target element');
229227
}
230228

231229
const handleClick = async (): Promise<void> => {
@@ -242,9 +240,9 @@ export class Feedback2 implements Integration {
242240
* Creates a new widget. Accepts partial options to override any options passed to constructor.
243241
*/
244242
public createWidget(
245-
optionOverrides: OptionalFeedbackConfiguration & { shouldCreateActor?: boolean } = {},
243+
optionOverrides: OverrideFeedbackConfiguration & { shouldCreateActor?: boolean } = {},
246244
): Promise<DialogComponent> {
247-
const options = mergeOptions(this.options, optionOverrides);
245+
const options = { ...this.options, ...optionOverrides };
248246

249247
return this._loadAndRenderDialog(options);
250248
}
@@ -309,6 +307,9 @@ export class Feedback2 implements Integration {
309307
const { colorScheme, themeDark, themeLight } = options;
310308
const shadow = host.attachShadow({ mode: 'open' });
311309
shadow.appendChild(
310+
// TODO: inject main styles as part of actor and dialog styles
311+
// therefore each render root can have it's own theme
312+
// err, everything can just have it's own shadowroot...
312313
createMainStyles(colorScheme, {
313314
themeDark,
314315
themeLight,
@@ -338,10 +339,13 @@ export class Feedback2 implements Integration {
338339
client.getIntegrationByName<ReturnType<typeof feedback2ScreenshotIntegration>>('Feedback2Screenshot');
339340
const { showScreenshot } = this.options;
340341

341-
if (showScreenshot === false && screenshotIntegration) {
342-
// Warn the user that they loaded too much and explicitly asked for screen shots to be off
343-
console.log('WARNING: youre not rendering screenshots but they are bundled into your application.'); // eslint-disable-line no-console
344-
}
342+
// Disable this because the site could have multiple feedback buttons, not all of them need to have screenshots enabled.
343+
// Must be a better way...
344+
//
345+
// if (showScreenshot === false && screenshotIntegration) {
346+
// // Warn the user that they loaded too much and explicitly asked for screen shots to be off
347+
// console.log('WARNING: Feedback2Screenshot is bundled but not rendered.'); // eslint-disable-line no-console
348+
// }
345349

346350
// START TEMP: Error messages
347351
console.log('ensureRenderer:', { modalIntegration, showScreenshot, screenshotIntegration }); // eslint-disable-line no-console

packages/feedback2/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export {
1616
Feedback2Screenshot,
1717
feedback2ScreenshotIntegration,
1818
} from './screenshot/integration';
19+
20+
export type { OverrideFeedbackConfiguration } from './types/index';

packages/feedback2/src/modal/components/Dialog.css.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,20 @@ export function createDialogStyles(): HTMLStyleElement {
4444
background-color: var(--background);
4545
color: var(--foreground);
4646
47-
width: 320px;
4847
max-width: 100%;
4948
max-height: calc(100% - 2rem);
5049
display: flex;
5150
flex-direction: column;
51+
gap: 16px;
5252
box-shadow: var(--box-shadow);
5353
transition: transform 0.2s ease-in-out;
5454
transform: translate(0, 0) scale(1);
55+
56+
padding: 24px;
57+
}
58+
.dialog__content-fullscreen {
59+
top: 16px;
60+
left: 16px;
5561
}
5662
5763
.dialog__header {
@@ -60,9 +66,8 @@ export function createDialogStyles(): HTMLStyleElement {
6066
justify-content: space-between;
6167
font-size: 20px;
6268
font-weight: 600;
63-
padding: 24px 24px 0 24px;
6469
margin: 0;
65-
margin-bottom: 16px;
70+
6671
}
6772
6873
.brand-link {
@@ -71,24 +76,29 @@ export function createDialogStyles(): HTMLStyleElement {
7176
7277
.error {
7378
color: var(--error);
74-
margin-bottom: 16px;
7579
}
7680
7781
.form {
7882
display: flex;
7983
overflow: auto;
80-
flex-direction: column;
84+
flex-direction: row;
8185
gap: 16px;
82-
padding: 0 24px 24px;
8386
flex: 1 0 auto;
8487
}
8588
86-
.form__error-container {
87-
color: var(--error);
89+
.form__right {
90+
width: 272px;
91+
display: flex;
92+
overflow: auto;
93+
flex-direction: column;
94+
justify-content: space-between;
95+
gap: 16px;
96+
8897
}
8998
90-
.form__error-container--hidden {
91-
display: none;
99+
100+
.form__error-container {
101+
color: var(--error);
92102
}
93103
94104
.form__label {
@@ -179,6 +189,13 @@ export function createDialogStyles(): HTMLStyleElement {
179189
}
180190
181191
.success-message {
192+
position: fixed;
193+
left: var(--left);
194+
right: var(--right);
195+
bottom: var(--bottom);
196+
top: var(--top);
197+
z-index: var(--z-index);
198+
182199
background-color: var(--background);
183200
border: var(--border);
184201
border-radius: var(--border-radius);

packages/feedback2/src/modal/components/Dialog.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ function DialogContainer({ open, onFormSubmitted, ...props }: Props & { open: bo
103103
<dialog class="dialog" onClick={props.onFormClose} open={open}>
104104
<DialogContent {...props}>
105105
<DialogHeader {...props} />
106-
<div style={{ display: 'flex', flexDirection: 'row', gap: '8px' }}>
107-
<Form {...props} onSubmitSuccess={onSubmitSuccess} />
108-
</div>
106+
<Form {...props} onSubmitSuccess={onSubmitSuccess} />
109107
</DialogContent>
110108
</dialog>
111109
)}

packages/feedback2/src/modal/components/Form.tsx

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -127,73 +127,78 @@ export function Form({
127127

128128
return (
129129
<form class="form" onSubmit={handleSubmit}>
130-
{error ? <div class="form__error-container">{error}</div> : null}
131-
132130
{ScreenshotInput && includeScreenshot ? <ScreenshotInput initialImage={undefined} /> : null}
133131

134-
{showName ? (
135-
<label for="name" class="form__label">
136-
<LabelText label={nameLabel} isRequired={isNameRequired} />
137-
<input
138-
class="form__input"
139-
defaultValue={defaultName}
140-
id="name"
141-
name="name"
142-
placeholder={namePlaceholder}
143-
required={isNameRequired}
144-
type="text"
145-
/>
146-
</label>
147-
) : (
148-
<input aria-hidden value={defaultName} name="name" type="hidden" />
149-
)}
150-
151-
{showEmail ? (
152-
<label for="email" class="form__label">
153-
<LabelText label={emailLabel} isRequired={isEmailRequired} />
154-
<input
155-
class="form__input"
156-
defaultValue={defaultEmail}
157-
id="email"
158-
name="email"
159-
placeholder={emailPlaceholder}
160-
required={isEmailRequired}
161-
type="text"
162-
></input>
163-
</label>
164-
) : (
165-
<input aria-hidden value={defaultEmail} name="email" type="hidden" />
166-
)}
167-
168-
<label for="message" class="form__label">
169-
<LabelText label={messageLabel} isRequired />
170-
<textarea
171-
autoFocus
172-
class="form__input form__input--textarea"
173-
id="message"
174-
name="message"
175-
placeholder={messagePlaceholder}
176-
required={true}
177-
rows={5}
178-
/>
179-
</label>
180-
181-
{ScreenshotToggle ? (
182-
<ScreenshotToggle
183-
onClick={() => {
184-
setIncludeScreeshot(prev => !prev);
185-
}}
186-
isScreenshotIncluded={includeScreenshot}
187-
/>
188-
) : null}
189-
190-
<div class="btn-group">
191-
<button class="btn btn--primary" type="submit">
192-
{submitButtonLabel}
193-
</button>
194-
<button class="btn btn--default" type="button" onClick={onFormClose}>
195-
{cancelButtonLabel}
196-
</button>
132+
<div class="form__right">
133+
<div class="form__top">
134+
{error ? <div class="form__error-container">{error}</div> : null}
135+
136+
{showName ? (
137+
<label for="name" class="form__label">
138+
<LabelText label={nameLabel} isRequired={isNameRequired} />
139+
<input
140+
class="form__input"
141+
defaultValue={defaultName}
142+
id="name"
143+
name="name"
144+
placeholder={namePlaceholder}
145+
required={isNameRequired}
146+
type="text"
147+
/>
148+
</label>
149+
) : (
150+
<input aria-hidden value={defaultName} name="name" type="hidden" />
151+
)}
152+
153+
{showEmail ? (
154+
<label for="email" class="form__label">
155+
<LabelText label={emailLabel} isRequired={isEmailRequired} />
156+
<input
157+
class="form__input"
158+
defaultValue={defaultEmail}
159+
id="email"
160+
name="email"
161+
placeholder={emailPlaceholder}
162+
required={isEmailRequired}
163+
type="text"
164+
></input>
165+
</label>
166+
) : (
167+
<input aria-hidden value={defaultEmail} name="email" type="hidden" />
168+
)}
169+
170+
<label for="message" class="form__label">
171+
<LabelText label={messageLabel} isRequired />
172+
<textarea
173+
autoFocus
174+
class="form__input form__input--textarea"
175+
id="message"
176+
name="message"
177+
placeholder={messagePlaceholder}
178+
required={true}
179+
rows={5}
180+
/>
181+
</label>
182+
183+
{ScreenshotToggle ? (
184+
<ScreenshotToggle
185+
onClick={() => {
186+
setIncludeScreeshot(prev => !prev);
187+
}}
188+
isScreenshotIncluded={includeScreenshot}
189+
/>
190+
) : null}
191+
</div>
192+
<div class="form__bottom">
193+
<div class="btn-group">
194+
<button class="btn btn--primary" type="submit">
195+
{submitButtonLabel}
196+
</button>
197+
<button class="btn btn--default" type="button" onClick={onFormClose}>
198+
{cancelButtonLabel}
199+
</button>
200+
</div>
201+
</div>
197202
</div>
198203
</form>
199204
);

packages/feedback2/src/screenshot/components/ScreenshotInput.css.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ export function createScreenshotInputStyles(): HTMLStyleElement {
77
const style = DOCUMENT.createElement('style');
88

99
style.textContent = `
10+
.dialog__content:has(.editor) {
11+
top: var(--bottom);
12+
left: var(--right);
13+
}
14+
1015
.editor {
1116
background: red;
12-
width: 75px;
13-
height: 75px;
17+
flex: 1 0 auto;
1418
}
1519
`;
1620

0 commit comments

Comments
 (0)