Skip to content

Commit b8b6ee8

Browse files
committed
Simplify feed creation page layout
1 parent 216352d commit b8b6ee8

7 files changed

Lines changed: 445 additions & 769 deletions

File tree

frontend/src/__tests__/App.test.tsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -83,24 +83,27 @@ describe('App', () => {
8383
});
8484
});
8585

86-
it('renders the streamlined hero and create section', () => {
86+
it('renders the radical-simple create flow', () => {
8787
render(<App />);
8888

89-
expect(screen.getByText('Create a feed URL.')).toBeInTheDocument();
90-
expect(screen.getByLabelText('Source URL')).toBeInTheDocument();
91-
expect(screen.getByText('Run your own instance')).toBeInTheDocument();
89+
expect(screen.getByLabelText('html2rss')).toBeInTheDocument();
90+
expect(screen.getByLabelText('Page URL')).toBeInTheDocument();
91+
expect(screen.getByRole('button', { name: 'More' })).toBeInTheDocument();
92+
expect(screen.queryByRole('link', { name: 'Bookmarklet' })).not.toBeInTheDocument();
9293
});
9394

94-
it('autofocuses the source url field', () => {
95+
it('autofocuses the source url field', async () => {
9596
render(<App />);
9697

97-
expect(document.activeElement).toBe(screen.getByLabelText('Source URL'));
98+
await waitFor(() => {
99+
expect(document.activeElement).toBe(screen.getByLabelText('Page URL'));
100+
});
98101
});
99102

100103
it('shows inline token prompt when submitting without a token', () => {
101104
render(<App />);
102105

103-
fireEvent.input(screen.getByLabelText('Source URL'), {
106+
fireEvent.input(screen.getByLabelText('Page URL'), {
104107
target: { value: 'https://example.com/articles' },
105108
});
106109
fireEvent.click(screen.getByRole('button', { name: 'Generate feed URL' }));
@@ -110,11 +113,6 @@ describe('App', () => {
110113
});
111114

112115
it('renders the result panel when a feed is available', async () => {
113-
vi.spyOn(window, 'fetch').mockResolvedValue({
114-
text: async () =>
115-
`<?xml version="1.0"?><rss><channel><title>Example Feed</title><item><title>Item One</title></item></channel></rss>`,
116-
} as Response);
117-
118116
mockUseFeedConversion.mockReturnValue({
119117
isConverting: false,
120118
result: {
@@ -132,13 +130,10 @@ describe('App', () => {
132130

133131
render(<App />);
134132

135-
expect(screen.getByText('Result')).toBeInTheDocument();
133+
expect(screen.getByText('Feed URL ready')).toBeInTheDocument();
136134
expect(screen.getByRole('button', { name: 'Create another feed' })).toBeInTheDocument();
137-
expect(screen.queryByText('Run your own instance')).not.toBeInTheDocument();
138-
139-
await waitFor(() => {
140-
expect(screen.getByText('Example Feed')).toBeInTheDocument();
141-
});
135+
expect(screen.queryByRole('link', { name: 'Bookmarklet' })).not.toBeInTheDocument();
136+
expect(screen.getByText('Example Feed')).toBeInTheDocument();
142137
});
143138

144139
it('surfaces conversion errors to the user', () => {
@@ -168,8 +163,7 @@ describe('App', () => {
168163

169164
render(<App />);
170165

171-
expect(screen.getByText('Utilities')).toBeInTheDocument();
172-
expect(screen.queryByText('Run your own instance')).not.toBeInTheDocument();
166+
fireEvent.click(screen.getByRole('button', { name: 'More' }));
173167
fireEvent.click(screen.getByRole('button', { name: 'Clear saved token' }));
174168

175169
expect(mockClearToken).toHaveBeenCalled();
@@ -178,7 +172,7 @@ describe('App', () => {
178172
it('saves access token and resumes feed creation from the inline prompt', async () => {
179173
render(<App />);
180174

181-
fireEvent.input(screen.getByLabelText('Source URL'), {
175+
fireEvent.input(screen.getByLabelText('Page URL'), {
182176
target: { value: 'https://example.com/articles' },
183177
});
184178
fireEvent.click(screen.getByRole('button', { name: 'Generate feed URL' }));
@@ -199,7 +193,7 @@ describe('App', () => {
199193
it('submits the token prompt with Enter', async () => {
200194
render(<App />);
201195

202-
fireEvent.input(screen.getByLabelText('Source URL'), {
196+
fireEvent.input(screen.getByLabelText('Page URL'), {
203197
target: { value: 'https://example.com/articles' },
204198
});
205199
fireEvent.click(screen.getByRole('button', { name: 'Generate feed URL' }));
@@ -217,7 +211,8 @@ describe('App', () => {
217211
window.history.replaceState({}, '', 'http://localhost:3000/frontend/index.html');
218212
render(<App />);
219213

220-
const bookmarklet = screen.getByRole('link', { name: 'Convert page to feed' });
214+
fireEvent.click(screen.getByRole('button', { name: 'More' }));
215+
const bookmarklet = screen.getByRole('link', { name: 'Bookmarklet' });
221216
expect(bookmarklet.getAttribute('href')).toContain('/frontend/index.html?url=');
222217
expect(bookmarklet.getAttribute('href')).not.toContain('%27+encodeURIComponent');
223218
});

frontend/src/__tests__/ResultDisplay.test.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,15 @@ describe('ResultDisplay', () => {
1616

1717
beforeEach(() => {
1818
vi.clearAllMocks();
19-
vi.spyOn(window, 'fetch').mockResolvedValue({
20-
text: async () =>
21-
`<?xml version="1.0"?><rss><channel><title>Example Feed</title><item><title>Item One</title></item></channel></rss>`,
22-
} as Response);
2319
});
2420

25-
it('renders utility actions and preview state', async () => {
21+
it('renders the simplified result actions', () => {
2622
render(<ResultDisplay result={mockResult} onCreateAnother={mockOnCreateAnother} />);
2723

28-
expect(screen.getByText('Result')).toBeInTheDocument();
24+
expect(screen.getByText('Feed URL ready')).toBeInTheDocument();
2925
expect(screen.getByText('Test Feed')).toBeInTheDocument();
30-
expect(
31-
screen.getByText('Copy the feed URL, then drop it into the reader or workflow you use.')
32-
).toBeInTheDocument();
3326
expect(screen.getByRole('button', { name: 'Copy feed URL' })).toBeInTheDocument();
3427
expect(screen.getByRole('link', { name: 'Open feed' })).toBeInTheDocument();
35-
36-
await waitFor(() => {
37-
expect(screen.getByText('Item One')).toBeInTheDocument();
38-
});
3928
});
4029

4130
it('calls onCreateAnother when the reset button is clicked', () => {

frontend/src/components/App.tsx

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect, useState } from 'preact/hooks';
22
import { ResultDisplay } from './ResultDisplay';
3-
import { CreateFeedPanel, InstanceInfo, type Strategy } from './AppPanels';
3+
import { CreateFeedPanel, UtilityStrip, type Strategy } from './AppPanels';
44
import { useAccessToken } from '../hooks/useAccessToken';
55
import { useApiMetadata } from '../hooks/useApiMetadata';
66
import { useFeedConversion } from '../hooks/useFeedConversion';
@@ -17,10 +17,7 @@ function BrandLockup() {
1717
<span />
1818
<span />
1919
</span>
20-
<div class="brand-lockup__text">
21-
<strong>html2rss</strong>
22-
<span>html to feed</span>
23-
</div>
20+
<strong class="brand-lockup__wordmark">html2rss</strong>
2421
</div>
2522
);
2623
}
@@ -141,7 +138,7 @@ export function App() {
141138

142139
if (metadataLoading || tokenLoading) {
143140
return (
144-
<section class="workspace-shell workspace-shell--loading">
141+
<section class="workspace-shell workspace-shell--centered workspace-shell--loading">
145142
<BrandLockup />
146143
<div class="status-card" aria-live="polite">
147144
<div class="status-card__spinner" aria-hidden="true" />
@@ -155,16 +152,9 @@ export function App() {
155152
}
156153

157154
return (
158-
<section class="workspace-shell">
159-
<header class={`workspace-frame${result ? ' workspace-frame--compact' : ''}`}>
160-
<div class="workspace-frame__masthead">
161-
<BrandLockup />
162-
</div>
163-
{!result && (
164-
<div class="workspace-frame__titleblock">
165-
<h1>Create a feed URL.</h1>
166-
</div>
167-
)}
155+
<section class="workspace-shell workspace-shell--centered">
156+
<header class="workspace-hero">
157+
<BrandLockup />
168158
</header>
169159

170160
{(metadataError || tokenStateError) && (
@@ -177,7 +167,7 @@ export function App() {
177167
{result ? (
178168
<ResultDisplay result={result} onCreateAnother={handleCreateAnother} />
179169
) : (
180-
<div class="workspace-grid">
170+
<>
181171
<CreateFeedPanel
182172
focusComposerKey={focusCreateComposerKey}
183173
feedFormData={feedFormData}
@@ -206,15 +196,14 @@ export function App() {
206196
}}
207197
strategyHint={strategyHint}
208198
/>
209-
210-
<InstanceInfo
199+
<UtilityStrip
211200
hasAccessToken={hasToken}
212201
onClearToken={() => {
213202
clearToken();
214203
setShowTokenPrompt(false);
215204
}}
216205
/>
217-
</div>
206+
</>
218207
)}
219208
</section>
220209
);

0 commit comments

Comments
 (0)