Skip to content

Commit 14ca9f9

Browse files
committed
feat: display view query text
1 parent 37ccfb5 commit 14ca9f9

File tree

10 files changed

+509
-13
lines changed

10 files changed

+509
-13
lines changed

package-lock.json

Lines changed: 279 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"react-redux": "^9.1.2",
4848
"react-router-dom": "^5.3.4",
4949
"react-split": "^2.0.14",
50+
"react-syntax-highlighter": "^15.6.1",
5051
"redux": "^5.0.1",
5152
"redux-location-state": "^2.8.2",
5253
"tslib": "^2.6.3",
@@ -136,6 +137,7 @@
136137
"@types/react": "^18.3.3",
137138
"@types/react-dom": "^18.3.0",
138139
"@types/react-router-dom": "^5.3.3",
140+
"@types/react-syntax-highlighter": "^15.5.13",
139141
"@types/uuid": "^10.0.0",
140142
"copyfiles": "^2.4.1",
141143
"http-proxy-middleware": "^2.0.6",

playwright.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const config: PlaywrightTestConfig = {
1919
: {
2020
command: 'npm run dev',
2121
port: 3000,
22+
reuseExistingServer: true,
2223
},
2324
use: {
2425
baseURL: baseUrl || 'http://localhost:3000/',
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.sql-highlighter {
2+
code {
3+
white-space: pre-wrap !important;
4+
5+
color: var(--g-color-text-primary) !important;
6+
}
7+
8+
pre {
9+
margin: 0 !important;
10+
11+
background: transparent !important;
12+
}
13+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {useThemeValue} from '@gravity-ui/uikit';
2+
import {PrismLight as SyntaxHighlighter} from 'react-syntax-highlighter';
3+
4+
import {cn} from '../../utils/cn';
5+
6+
import {dark, light, yql} from './yql';
7+
8+
import './SqlHighlighter.scss';
9+
10+
SyntaxHighlighter.registerLanguage('yql', yql);
11+
12+
const b = cn('sql-highlighter');
13+
14+
interface SqlHighlighterProps {
15+
children: string;
16+
className?: string;
17+
}
18+
19+
export const SqlHighlighter = ({children, className}: SqlHighlighterProps) => {
20+
const themeValue = useThemeValue();
21+
const isDark = themeValue === 'dark' || themeValue === 'dark-hc';
22+
23+
return (
24+
<div className={b(null, className)}>
25+
<SyntaxHighlighter language="yql" style={isDark ? dark : light}>
26+
{children}
27+
</SyntaxHighlighter>
28+
</div>
29+
);
30+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const keywords =
2+
'$row|$rows|action|add|all|alter|and|any|as|asc|assume|async|bigserial|serial|smallserial|serial8|serial4|serial2|begin|bernoulli|between|by|case|changefeed|column|columns|commit|compact|create|cross|cube|declare|define|delete|desc|dict|discard|distinct|do|drop|else|empty_action|end|erase|evaluate|exclusion|exists|export|family|flatten|for|from|full|global|group|grouping|having|if|ignore|ilike|import|in|index|inner|insert|into|is|join|key|left|like|limit|list|match|not|null|nulls|offset|on|only|optional|or|order|over|partition|pragma|presort|primary|process|reduce|regexp|repeatable|replace|respect|result|return|right|rlike|rollup|sample|schema|select|semi|set|sets|stream|subquery|sync|table|tablesample|then|truncate|union|intersect|except|update|upsert|use|using|values|view|when|where|window|with|without|xor|callable|resource|tagged|generic|unit|void|emptylist|emptydict|flow|callable|resource|tagged|generic|unit|void|emptylist|emptydict|flow'.split(
3+
'|',
4+
);
5+
export const typeKeywords =
6+
'bool|date|datetime|decimal|double|float|int16|int32|int64|int8|interval|json|jsondocument|string|timestamp|tzdate|tzdatetime|tztimestamp|uint16|uint32|uint64|uint8|utf8|uuid|yson|text|bytes'.split(
7+
'|',
8+
);
9+
10+
export const builtinFunctions =
11+
'abs|aggregate_by|aggregate_list|aggregate_list_distinct|agg_list|agg_list_distinct|as_table|avg|avg_if|adaptivedistancehistogram|adaptivewardhistogram|adaptiveweighthistogram|addmember|addtimezone|aggregateflatten|aggregatetransforminput|aggregatetransformoutput|aggregationfactory|asatom|asdict|asdictstrict|asenum|aslist|asliststrict|asset|assetstrict|asstruct|astagged|astuple|asvariant|atomcode|bitcast|bit_and|bit_or|bit_xor|bool_and|bool_or|bool_xor|bottom|bottom_by|blockwardhistogram|blockweighthistogram|cast|coalesce|concat|concat_strict|correlation|count|count_if|covariance|covariance_population|covariance_sample|callableargument|callableargumenttype|callableresulttype|callabletype|callabletypecomponents|callabletypehandle|choosemembers|combinemembers|countdistinctestimate|currentauthenticateduser|currentoperationid|currentoperationsharedid|currenttzdate|currenttzdatetime|currenttztimestamp|currentutcdate|currentutcdatetime|currentutctimestamp|dense_rank|datatype|datatypecomponents|datatypehandle|dictaggregate|dictcontains|dictcreate|dicthasitems|dictitems|dictkeytype|dictkeys|dictlength|dictlookup|dictpayloadtype|dictpayloads|dicttype|dicttypecomponents|dicttypehandle|each|each_strict|emptydicttype|emptydicttypehandle|emptylisttype|emptylisttypehandle|endswith|ensure|ensureconvertibleto|ensuretype|enum|evaluateatom|evaluatecode|evaluateexpr|evaluatetype|expandstruct|filter|filter_strict|find|first_value|folder|filecontent|filepath|flattenmembers|forceremovemember|forceremovemembers|forcerenamemembers|forcespreadmembers|formatcode|formattype|frombytes|frompg|funccode|greatest|grouping|gathermembers|generictype|histogram|hll|hoppingwindowpgcast|hyperloglog|if|if_strict|instanceof|json_exists|json_query|json_value|jointablerow|just|lag|last_value|lead|least|len|length|like|likely|like_strict|lambdaargumentscount|lambdacode|lambdaoptionalargumentscount|linearhistogram|listaggregate|listall|listany|listavg|listcode|listcollect|listconcat|listcreate|listdistinct|listenumerate|listextend|listextendstrict|listextract|listfilter|listflatmap|listflatten|listfold|listfold1|listfold1map|listfoldmap|listfromrange|listfromtuple|listhas|listhasitems|listhead|listindexof|listitemtype|listlast|listlength|listmap|listmax|listmin|listnotnull|listreplicate|listreverse|listskip|listskipwhile|listskipwhileinclusive|listsort|listsortasc|listsortdesc|listsum|listtake|listtakewhile|listtakewhileinclusive|listtop|listtopsort|listtopasc|listtopdesc|listtopsortasc|listtopsortdesc|listtotuple|listtype|listtypehandle|listunionall|listuniq|listzip|listzipall|loghistogram|logarithmichistogram|max|max_by|max_of|median|min|min_by|min_of|mode|multi_aggregate_by|nanvl|nvl|nothing|nulltype|nulltypehandle|optionalitemtype|optionaltype|optionaltypehandle|percentile|parsefile|parsetype|parsetypehandle|pgand|pgarray|pgcall|pgconst|pgnot|pgop|pgor|pickle|quotecode|range|range_strict|rank|regexp|regexp_strict|rfind|row_number|random|randomnumber|randomuuid|removemember|removemembers|removetimezone|renamemembers|replacemember|reprcode|resourcetype|resourcetypehandle|resourcetypetag|some|stddev|stddev_population|stddev_sample|substring|sum|sum_if|sessionstart|sessionwindow|setcreate|setdifference|setincludes|setintersection|setisdisjoint|setsymmetricdifference|setunion|spreadmembers|stablepickle|startswith|staticmap|staticzip|streamitemtype|streamtype|streamtypehandle|structmembertype|structmembers|structtypecomponents|structtypehandle|subqueryextend|subqueryextendfor|subquerymerge|subquerymergefor|subqueryunionall|subqueryunionallfor|subqueryunionmerge|subqueryunionmergefor|top|topfreq|top_by|tablename|tablepath|tablerecordindex|tablerow|tablerows|taggedtype|taggedtypecomponents|taggedtypehandle|tobytes|todict|tomultidict|topg|toset|tosorteddict|tosortedmultidict|trymember|tupleelementtype|tupletype|tupletypecomponents|tupletypehandle|typehandle|typekind|typeof|udaf|udf|unittype|unpickle|untag|unwrap|variance|variance_population|variance_sample|variant|varianttype|varianttypehandle|variantunderlyingtype|voidtype|voidtypehandle|way|worldcode|weakfield'.split(
12+
'|',
13+
);
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import {
2+
vscDarkPlus as darkTheme,
3+
materialLight as lightTheme,
4+
} from 'react-syntax-highlighter/dist/esm/styles/prism';
5+
6+
import {builtinFunctions, keywords, typeKeywords} from './keywords';
7+
8+
export const light = {
9+
...lightTheme,
10+
'pre[class*="language-"]': {
11+
...lightTheme['pre[class*="language-"]'],
12+
background: 'transparent',
13+
},
14+
'code[class*="language-"]': {
15+
...lightTheme['code[class*="language-"]'],
16+
background: 'transparent',
17+
},
18+
comment: {
19+
color: '#969896',
20+
},
21+
string: {
22+
color: '#a31515',
23+
},
24+
tablepath: {
25+
color: '#338186',
26+
},
27+
function: {
28+
color: '#7a3e9d',
29+
},
30+
udf: {
31+
color: '#7a3e9d',
32+
},
33+
type: {
34+
color: '#4d932d',
35+
},
36+
boolean: {
37+
color: '#608b4e',
38+
},
39+
constant: {
40+
color: '#608b4e',
41+
},
42+
variable: {
43+
color: '#001188',
44+
},
45+
};
46+
47+
export const dark = {
48+
...darkTheme,
49+
'pre[class*="language-"]': {
50+
...darkTheme['pre[class*="language-"]'],
51+
background: 'transparent',
52+
},
53+
'code[class*="language-"]': {
54+
...darkTheme['code[class*="language-"]'],
55+
background: 'transparent',
56+
},
57+
comment: {
58+
color: '#969896',
59+
},
60+
string: {
61+
color: '#ce9178',
62+
},
63+
tablepath: {
64+
color: '#338186',
65+
},
66+
function: {
67+
color: '#9e7bb0',
68+
},
69+
udf: {
70+
color: '#9e7bb0',
71+
},
72+
type: {
73+
color: '#6A8759',
74+
},
75+
boolean: {
76+
color: '#608b4e',
77+
},
78+
constant: {
79+
color: '#608b4e',
80+
},
81+
variable: {
82+
color: '#74b0df',
83+
},
84+
};
85+
86+
export function yql(Prism: any) {
87+
// Define YQL language
88+
Prism.languages.yql = {
89+
comment: [
90+
{
91+
pattern: /--.*$/m,
92+
greedy: true,
93+
},
94+
{
95+
pattern: /\/\*[\s\S]*?(?:\*\/|$)/,
96+
greedy: true,
97+
},
98+
],
99+
tablepath: {
100+
pattern: /(`[\w/]+`\s*\.\s*)?`[^`]+`/,
101+
greedy: true,
102+
},
103+
string: [
104+
{
105+
pattern: /'(?:\\[\s\S]|[^\\'])*'/,
106+
greedy: true,
107+
},
108+
{
109+
pattern: /"(?:\\[\s\S]|[^\\"])*"/,
110+
greedy: true,
111+
},
112+
{
113+
pattern: /@@(?:[^@]|@(?!@))*@@/,
114+
greedy: true,
115+
},
116+
],
117+
variable: [
118+
{
119+
pattern: /\$[a-zA-Z_]\w*/,
120+
greedy: true,
121+
},
122+
],
123+
function: {
124+
pattern: new RegExp(`\\b(?:${builtinFunctions.join('|')})\\b`, 'i'),
125+
greedy: true,
126+
},
127+
keyword: {
128+
pattern: new RegExp(`\\b(?:${keywords.join('|')})\\b`, 'i'),
129+
greedy: true,
130+
},
131+
udf: {
132+
pattern: /[A-Za-z_]\w*::[A-Za-z_]\w*/,
133+
greedy: true,
134+
},
135+
type: {
136+
pattern: new RegExp(`\\b(?:${typeKeywords.join('|')})\\b`, 'i'),
137+
greedy: true,
138+
},
139+
boolean: {
140+
pattern: /\b(?:true|false|null)\b/i,
141+
greedy: true,
142+
},
143+
number: {
144+
pattern: /[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,
145+
greedy: true,
146+
},
147+
operator: {
148+
pattern: /[-+*/%<>!=&|^~]+|\b(?:and|or|not|is|like|ilike|rlike|in|between)\b/i,
149+
greedy: true,
150+
},
151+
punctuation: {
152+
pattern: /[;[\](){}.,]/,
153+
greedy: true,
154+
},
155+
};
156+
}
157+
158+
yql.displayName = 'yql';
159+
yql.aliases = ['yql'] as string[];

src/components/TruncatedQuery/TruncatedQuery.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22

33
import {cn} from '../../utils/cn';
44
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';
5+
import {SqlHighlighter} from '../SqlHighlighter/SqlHighlighter';
56

67
import './TruncatedQuery.scss';
78

@@ -22,12 +23,12 @@ export const TruncatedQuery = ({value = '', maxQueryHeight = 6}: TruncatedQueryP
2223
'\n...\nThe request was truncated. Click on the line to show the full query on the query tab';
2324
return (
2425
<React.Fragment>
25-
<span className={b()}>{content}</span>
26+
<SqlHighlighter className={b()}>{content}</SqlHighlighter>
2627
<span className={b('message', {color: 'secondary'})}>{message}</span>
2728
</React.Fragment>
2829
);
2930
}
30-
return <React.Fragment>{value}</React.Fragment>;
31+
return <SqlHighlighter>{value}</SqlHighlighter>;
3132
};
3233

3334
interface OneLineQueryWithPopoverProps {
@@ -36,8 +37,11 @@ interface OneLineQueryWithPopoverProps {
3637

3738
export const OneLineQueryWithPopover = ({value = ''}: OneLineQueryWithPopoverProps) => {
3839
return (
39-
<CellWithPopover contentClassName={b('popover-content')} content={value}>
40-
{value}
40+
<CellWithPopover
41+
contentClassName={b('popover-content')}
42+
content={<SqlHighlighter>{value}</SqlHighlighter>}
43+
>
44+
<SqlHighlighter>{value}</SqlHighlighter>
4145
</CellWithPopover>
4246
);
4347
};

src/containers/Tenant/Info/View/View.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {DefinitionListItem} from '@gravity-ui/components';
2-
import {Text} from '@gravity-ui/uikit';
32

3+
import {SqlHighlighter} from '../../../../components/SqlHighlighter/SqlHighlighter';
44
import {YDBDefinitionList} from '../../../../components/YDBDefinitionList/YDBDefinitionList';
55
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
66
import {getEntityName} from '../../utils';
@@ -13,11 +13,7 @@ const prepareViewItems = (data: TEvDescribeSchemeResult): DefinitionListItem[] =
1313
{
1414
name: i18n('view.query-text'),
1515
copyText: queryText,
16-
content: (
17-
<Text variant="code-2" wordBreak="break-word">
18-
{queryText}
19-
</Text>
20-
),
16+
content: queryText ? <SqlHighlighter>{queryText}</SqlHighlighter> : null,
2117
},
2218
];
2319
};

tests/suites/tenant/queryHistory/queryHistory.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ test.describe('Query History', () => {
3434

3535
// Check if the query appears in the history
3636
const historyTable = page.locator('.ydb-queries-history table');
37-
await expect(historyTable.locator(`text="${testQuery}"`)).toBeVisible({
37+
await expect(historyTable.locator('.sql-highlighter', {hasText: testQuery})).toBeVisible({
3838
timeout: VISIBILITY_TIMEOUT,
3939
});
4040
});
@@ -85,6 +85,6 @@ test.describe('Query History', () => {
8585

8686
// Check if the query appears in the history
8787
const historyTable = page.locator('.ydb-queries-history table');
88-
await expect(historyTable.locator(`text="${testQuery}"`)).toBeVisible();
88+
await expect(historyTable.locator('.sql-highlighter', {hasText: testQuery})).toBeVisible();
8989
});
9090
});

0 commit comments

Comments
 (0)