fix(results): vertical scroll + reliable ⌘A in the raw output pane by BorisTyshkevich · Pull Request #2 · Altinity/altinity-sql-browser · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions src/styles.css
6 changes: 5 additions & 1 deletion src/ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,11 @@ export function renderApp(app, helpers) {
h('button', { class: 'hd-btn', title: 'Keyboard shortcuts (?)', onclick: () => app.actions.openShortcuts() }, Icon.shortcuts()),
app.dom.themeBtn,
h('div', { class: 'user-email', title: app.email() }, app.email()),
h('button', { class: 'hd-btn text', title: 'Sign out', onclick: () => app.signOut() }, 'Sign out'));
h('button', { class: 'hd-btn text', title: 'Log out', onclick: () => app.signOut() }, 'Log Out'),
h('a', {
class: 'hd-btn', href: 'https://github.com/Altinity/altinity-sql-browser',
target: '_blank', rel: 'noopener noreferrer', title: 'View source on GitHub',
}, Icon.github()));

app.dom.schemaSearchInput = h('input', {
type: 'text', placeholder: 'Search tables, columns…',
Expand Down
8 changes: 5 additions & 3 deletions src/ui/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ export function svg(d, w = 12, hgt = 12, opts = {}) {
return el;
}

/** Single-path filled icon. */
export function svgFilled(d, w = 12, hgt = 12) {
/** Single-path filled icon. `vbW`/`vbH` default to the display size, but can
* differ when the path is authored in a different coordinate space. */
export function svgFilled(d, w = 12, hgt = 12, vbW = w, vbH = hgt) {
const el = document.createElementNS(NS, 'svg');
el.setAttribute('width', w);
el.setAttribute('height', hgt);
el.setAttribute('viewBox', `0 0 ${w} ${hgt}`);
el.setAttribute('viewBox', `0 0 ${vbW} ${vbH}`);
el.setAttribute('fill', 'currentColor');
const path = document.createElementNS(NS, 'path');
path.setAttribute('d', d);
Expand Down Expand Up @@ -78,4 +79,5 @@ export const Icon = {
json: () => svg('M4 1.5C2.5 1.5 2.5 3 2.5 4S2.5 5 1.5 6c1 1 1 2 1 2s0 1.5 1.5 1.5M8 1.5c1.5 0 1.5 1.5 1.5 2.5s0 1 1 2c-1 1-1 2-1 2s0 1.5-1.5 1.5', 12, 12),
table2: () => iconEl('<rect x="1.5" y="2" width="9" height="8" rx=".5"/><path d="M1.5 4.5h9M1.5 7h9M4.5 4.5v5"/>', 12, 12),
shortcuts: () => iconEl('<rect x="1.5" y="3" width="9" height="6" rx="1"/><path d="M3.5 5h.01M6 5h.01M8.5 5h.01M3.5 7h5"/>', 12, 12, 1.3),
github: () => svgFilled('M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12', 15, 15, 24, 24),
};
7 changes: 1 addition & 6 deletions src/ui/saved-history.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { h } from './dom.js';
import { Icon } from './icons.js';
import { timeAgo } from '../core/format.js';
import { deleteSaved, deleteHistory, clearHistory } from '../state.js';
import { deleteSaved, deleteHistory } from '../state.js';

export function renderSavedHistory(app) {
const tabsRow = app.dom.savedTabsRow;
Expand Down Expand Up @@ -53,11 +53,6 @@ function renderHistory(app, list) {
list.appendChild(h('div', { class: 'saved-empty' }, 'No history yet.'));
return;
}
list.appendChild(h('div', { class: 'list-head' },
h('button', {
class: 'clear-btn', title: 'Clear all history',
onclick: () => { clearHistory(state, app.saveJSON); renderSavedHistory(app); },
}, 'Clear history')));
for (const ent of state.history) {
list.appendChild(h('div', { class: 'history-row', onclick: () => app.actions.loadIntoNewTab('From history', ent.sql) },
h('button', {
Expand Down
12 changes: 8 additions & 4 deletions src/ui/shortcuts.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ export function handleKeydown(e, app) {
return 'toggleSaved';
}
if (mod && e.key.toLowerCase() === 'a') {
// Inside a raw result pane (TSV / JSON output), select just that text so it
// can be copied — not the whole page. Elsewhere (e.g. the editor textarea)
// fall through to the browser's native select-all.
// When a raw result pane (TSV / JSON output) is on screen and the user isn't
// typing, ⌘/Ctrl+A selects just that text so it can be copied — not the whole
// page. Keyed off "not editing + pane present" rather than pane focus, because
// macOS WebKit doesn't focus a tabindex <div> on click (so e.target stays
// <body>). A focused editor/input keeps the native select-all (whole query).
const t = e.target;
const box = t && t.closest && t.closest('.raw-text-view, .json-view');
if (t && (t.tagName === 'TEXTAREA' || t.tagName === 'INPUT' || t.isContentEditable)) return null;
const doc = (t && t.ownerDocument) || document;
const box = doc.querySelector('.raw-text-view, .json-view');
if (!box) return null;
e.preventDefault();
box.ownerDocument.defaultView.getSelection().selectAllChildren(box);
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ describe('renderApp shell', () => {
expect(e.sessionStorage.getItem('oauth_id_token')).toBeNull();
expect(app.root.querySelector('.login-screen')).not.toBeNull();
});
it('header has a Log Out button and a GitHub source link', () => {
const { app } = rendered();
expect(app.root.querySelector('.hd-btn.text').textContent).toContain('Log Out');
const gh = app.root.querySelector('a.hd-btn[href*="github.com"]');
expect(gh).not.toBeNull();
expect(gh.getAttribute('target')).toBe('_blank');
expect(gh.getAttribute('rel')).toContain('noopener');
expect(gh.querySelector('svg')).not.toBeNull();
});
it('setTokens clears the one-shot pkce verifier and csrf state', () => {
const e = env({ sessionStorage: memSession({ oauth_verifier: 'v', oauth_state: 's' }) });
const app = createApp(e);
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/icons.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ describe('svgFilled', () => {
const el = svgFilled('M0 0z', 16, 16);
expect(el.getAttribute('fill')).toBe('currentColor');
expect(el.getAttribute('width')).toBe('16');
expect(el.getAttribute('viewBox')).toBe('0 0 16 16');
});
it('honours an explicit viewBox distinct from the display size', () => {
const el = svgFilled('M0 0z', 15, 15, 24, 24);
expect(el.getAttribute('width')).toBe('15');
expect(el.getAttribute('viewBox')).toBe('0 0 24 24');
});
});

Expand Down
11 changes: 0 additions & 11 deletions tests/unit/saved-history.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,6 @@ describe('renderSavedHistory', () => {
expect(app.dom.savedList.querySelectorAll('.history-row')).toHaveLength(1);
});

it('history: clear button empties all history', () => {
const app = makeApp();
app.state.sidePanel = 'history';
app.state.history = [{ id: 'h1', sql: 'SELECT 1', ts: Date.now(), rows: 3, ms: 4 }];
renderSavedHistory(app);
click(app.dom.savedList.querySelector('.clear-btn'));
expect(app.state.history).toEqual([]);
expect(app.saveJSON).toHaveBeenCalled();
expect(app.dom.savedList.textContent).toContain('No history yet.');
});

it('switching panels persists the choice', () => {
const app = makeApp();
app.state.sidePanel = 'saved';
Expand Down
16 changes: 11 additions & 5 deletions tests/unit/shortcuts.test.js
Loading