Expand/Collapse with Ant Typography - antd

Ant Design has a Typography library which is helpful in displaying text. One particularly helpful feature is the ability to "Expand" text. From the Ant documentation, you can display a paragraph as such:
Hitting "Expand" will produce the following:
The issue here, is that after you expand there is no option to "Collapse" the text back to what we had originally, which can be problematic. I have been looking through the Typography documentation here, but there is no mention of the ability to collapse. I was wondering if there was a way to achieve something like a "Collapse" using the Ant Typography library.
Another option that I have considered is the react-text-collapse library, however I am not sure it is a perfect solution.

Looks like ant design doesn't have default option to collapse the typography paragraph. You can request for the feature at ant design github.
However you can achieve it with some state changes.
Here is the sample link
https://codesandbox.io/s/upbeat-jepsen-4h7ek
class Demo extends React.Component {
state = {
expand: false,
counter: 0
};
typoExpand = () => {
this.setState({
expand: true,
counter: !this.state.expand
? this.state.counter + 0
: this.state.counter + 1
});
};
typoClose = () => {
this.setState({
expand: false,
counter: !this.state.expand
? this.state.counter + 0
: this.state.counter + 1
});
};
renderParagraph() {
return (
<div key={this.state.counter}>
<Paragraph
ellipsis={{
rows: 3,
expandable: true,
onExpand: this.typoExpand
}}
>
Ant Design, a design language for background applications, is refined
by Ant UED Team. Ant Design, a design language for background
applications, is refined by Ant UED Team. Ant Design, a design
language for background applications, is refined by Ant UED Team. Ant
Design, a design language for background applications, is refined by
Ant UED Team. Ant Design, a design language for background
applications, is refined by Ant UED Team. Ant Design, a design
language for background applications, is refined by Ant UED Team.
</Paragraph>
</div>
);
}
render() {
return (
<div>
{this.renderParagraph()}
{this.state.expand && <a onClick={this.typoClose}>Close</a>}
</div>
);
}
}

You can just add a state to the target Typography, for example:
const { Paragraph, Text } = Typography;
const App = () => {
const [expandable, setExpandable] = useState(true);
const toggle = () => setExpandable(p => !p);
return (
// v Style the container for your needs
<div>
<Paragraph ellipsis={{ rows: 3, expandable }}>
Ant Design, a design language for background applications, is refined by
Ant UED Team. Ant Design, a design language for background applications,
is refined by Ant UED Team. Ant Design, a design language for background
applications, is refined by Ant UED Team. Ant Design, a design language
for background applications, is refined by Ant UED Team. Ant Design, a
design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by
Ant UED Team.
</Paragraph>
<Text code onClick={toggle}>
Expand
</Text>
</div>
);
};
What's left is to style the container (which depends on you), like making a flexbox (antd got Grid implemented) so the Expand action will be "at the end" of the paragraph (and not a block as in the example).

Related

What is the difference between bind:after and bind:set in Blazor 7?

Recently, in Blazor 7, a feature has been added to make it easier to bind and call the method based on changes in the bound expression.
In .NET 7, you can now easily run async logic after a binding event has completed using the new #bind:after modifier:
<input #bind="searchText" #bind:after="PerformSearch" />
#code {
string searchText = "";
async Task PerformSearch()
{
// Do something async with searchText
}
}
In this example, the PerformSearch async method runs automatically after any changes to the search text are detected.
Another method has been added too. The #bind:get and #bind:set modifiers are always used together. The #bind:get modifier specifies the value to bind to, and the #bind:set modifier specifies a callback that's called when the value changes.
The questions are:
What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"? Both of these seem to call the PerformSearch after the searchText is changed.
Where is the use of each?
What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"?
You should only use #bind:after="PerformSearch" with #bind="searchText",in which case the bind will set the value of searchText, so you shouldn't also try and set it in PerformSearch.
If you use #bind:set="PerformSearch" then you must set the value of searchText in PerformSearch, and use #bind:get="searchText".
Where is the use of each?
The MS Docs article I think gives a good guide. It all depends on your knowledge level on components.
It's important to understand two points:
This is Razor syntax, not C#.
It's just syntactic sugar: high level functionality, shorthand Razor directives to encapsulate existing functionality.
Also note:
There's been a lot of MS Blazor Team activity on this subject since 7.0 was released. See https://github.com/dotnet/aspnetcore/issues/44957 for details on problems with the way the Razor compiler treats the #bind directives.
There are some updates to the MS Docs on the subject - https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#blazor-custom-elements.
Here's my demo page for this answer.
#page "/"
<PageTitle>Index</PageTitle>
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
<input class="form-control mb-3" type="text" #bind="this.Value" #bind:after="DoSearch" #bind:event="oninput"/>
<div class="alert alert-info m-2 p-2">
#Value
</div>
<div class="alert alert-primary m-2 p-2">
#message
</div>
#code {
private string? Value;
private string message = "Not Set";
private async Task DoSearch()
{
await Task.Delay(1000);
message= $"Set at {DateTime.Now.ToLongTimeString()}";
}
private void ValueSetter(string __value)
=> this.Value = __value;
private Task SearchSetter(string __value)
{
this.searchText = __value;
return DoSearch();
}
}
Let's look at the actual C# code the Razor compiler builds.
This is the code snippet when just using bind:set=this.ValueSetter:
__builder.AddAttribute(8, "oninput", EventCallback.Factory.CreateBinder(
this,
CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: this.ValueSetter,
value: this.Value
),
this.Value));
This simply calls the setter delegate assigned to set.
This is the code snippet when using :bind=this.Value and #bind:after=DoSearch:
__builder.AddAttribute(14, "oninput", EventCallback.Factory.CreateBinder(
this, CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: __value => {
this.Value = __value;
return RuntimeHelpers.InvokeAsynchronousDelegate(callback: DoSearch);
},
value: this.Value),
this.Value));
It's a little more complicated. The compiler builds the equivalent to this:
Task AnonymousMethod(string __value)
{
this.Value = __value;
return DoSearch()
}
A Note on Development Environment Errors
Depending on your development environment, you will get errors with certain combinations. Some of which at the moment appear to be misleading or totally wrong. They will be fixed shortly.
Quote from Dan Roth: Hi folks. The VS fix for this just missed the window for 17.4.4 but should be addressed in the next VS patch update in February. We apologize for the wait and thank you for your patience!
In Visual Studio.
This is a correct error:
<InputText class="form-control" #bind-Value:get="this.searchText" #bind-Value:set="this.SetSearchText" #bind-Value:after="DoSearch" />
Severity Code Description Project File Line Suppression State
Error (active) RZ10019 Attribute 'bind-Value:after' can not be used with 'bind-Value:set'. Invoke the code in 'bind-Value:after' inside 'bind-Value:set' instead.
While this is bull!
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
And while it gives this error compiles and runs!
Severity Code Description Project File Line Suppression State
Error (active) CS1503 Argument 3: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'System.Action<string?>'
And this line:
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
Compiles but is obviously also total bull.
__builder.AddMarkupContent(9, "\r\n\r\n<input class=\"form-control mb-3\" type=\"text\" #bind:get=\"this.Value\" #bind:after=\"ValueSetter\">\r\n\r\n");
Why is it #bind:get+#bind:set and not just #bind+#bind:set?
Because if you see <input #bind="#val" #bind:set="#MyMethod" /> often, it creates confusion:
It looks as if the #bind:set is what makes it a two-way binding, and that you could make it one-way by removing that. Whereas in fact that would be wrong (you'd still have a two-way binding, just one that behaves differently now).
It looks as if it would be equivalent to write <input value="#val" #bind:set="#MyMethod />, and it almost is, but not quite because the formatting logic would differ. Much better not to create the ambiguity and have one correct solution.
We can avoid the above problems by having a compiler rule that #bind:get and #bind:set must always be used as a pair - you can't just have one of them and not the other (nor can you have them with #bind). So none of the weird cases will arise.
Couldn't you use #bind:set to achieve (in effect) #bind:after, and hence we don't need #bind:after?
Theoretically yes. You could #bind:set to a method that writes to your field and then runs your async logic. However, this is far less obvious for newcomers, and is less convenient in common cases. And it invites mistakes: if you do something async before setting the field, the UI will temporarily revert and generally behave badly. So it's valuable to have #bind:after for convenience and to guide correct usage. We can regard #bind:get/#bind:set as a more advanced case mainly for people implementing bindable components, as it gives them a really clean and safe solution, and such developers are advanced enough to understand that they just shouldn't do async work before calling ValueChanged.
Can you use all three at once, e.g., <input #bind:get="#value" #bind:set="#MyMethod" #bind:after="#DoStuff" />?
Sure, why not? I think that the generated logic should await MyMethod before calling DoStuff, since "after" feels like it means "after all the work involved in calling set". It's an edge case but I can't think of any problems this will cause nor any major increase in implementation cost.
Do other #bind modifiers like #bind:event and #bind:format work with this?
Yes, and that's partly why it's a big improvement over manual value/onchange pairs.
you can refer this link get more idea https://github.com/dotnet/aspnetcore/issues/39837

Correctly showing RTL languages in JIRA

Does anybody knows how to use JIRA with RTL languages?
It seems that their interface is not configurable to change the direction of text flow.
There is server side Jira plugin for this.
Right To Left Plugin for Jira
Thought I did not try it and of cause that means buying, installing etc.
There is also a chrome plugin
JiraRTL
(Disclaimer I am the creator of this plugin. And by the way, help wanted for maintaining it.)
I use Jira announcement banner to inject custom CSS or JS: How to add custom JS and CSS
Just add this code:
<script>
setInterval(function () {
let pegah_dirAutoSupportSelector =
".user-content-block:not([dir])";
const items = document.querySelectorAll(pegah_dirAutoSupportSelector);
items.forEach((x) => {
x.setAttribute("dir", "auto");
});
}, 1000);
</script>

How add react-relay component to the storybook?

I am trying create a storybook for my react-realy app, but i don't know how to set mockup data for that component. For simple a component it is ok, because i can use dummy UI component vs Container approach, but i can't use this for nested relay components, for example there is a UserList component, which i want add to storybook, i can split relay fragment part to container and UI part to the component, but what if UserList children are too relay component? I can't split their when they are a part of the composition of UserList?
Is there some solution for add relay components to the storybook?
I created a NPM package called use-relay-mock-environment, which is based on relay-test-utils which allows you to make Storybook stories out of your Relay components.
It allows nesting of Relay components, so you can actually make stories for full pages made out of Relay components. Here's an example:
// MyComponent.stories.(js | jsx | ts | tsx)
import React from 'react';
import { RelayEnvironmentProvider } from 'react-relay';
import createRelayMockEnvironmentHook from 'use-relay-mock-environment';
import MyComponent from './MyComponentQuery';
const useRelayMockEnvironment = createRelayMockEnvironmentHook({
// ...Add global options here (optional)
});
export default {
title: 'MyComponent',
component: MyComponent,
};
export const Default = () => {
const environment = useRelayMockEnvironment({
// ...Add story specific options here (optional)
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
export const Loading = () => {
const environment = useRelayMockEnvironment({
forceLoading: true
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
You can also add <RelayEnvironmentProvider /> as a decorator, but I recommend not doing that if you want to create multiple stories for different states/mock data. In the above example I show 2 stories, the Default one, and a Loading one.
Not only that, it requires minimal coding, where you don't need to add the #relay-test-operation directive to your query, and the mocked data is automatically generated for you using faker.js, allowing you to focus on what matters, which is building great UI.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!
I also created relay-butler, which is a CLI that takes in GraphQL fragments and outputs Relay components, including a auto-generated query component that wraps the fragment component, and Storybook stories (the Default and Loading ones by default) that wrap that query component. And literally within minutes, I can create beautiful Relay components that are "documented" within Storybook.
Would also love some feedback for it!

google translator toolkit - how to exclude portions of the text from being processed

Does anybody know whether there is a possiblity to exclude portions of a text from being processed by the google translator toolkit?
A great advantage of this tool is that it suggests the translations of sentences which have already been translated in another context. However, if any additional footnote and/or remark has been added to the text, it won't be recognized as a match. I am looking for a possibility to enclose such text in "brackets" within which it will be ignored.
For example, the following two strings should be recognized as identical:
"This is one continuous sentence."
"This is {this text will be ignored}one continuous sentence."
and be translated i.e. into German as:
"Dies ist ein zusammenhängender Satz."
"Dies is {this text will be ignored}ein zusammenhängender Satz."
If neccessary I could number such insertions and place their content into additional paragraphs like:
"This is one continuous sentence."
"This is {1}one continuous sentence."
"{1 this text will be ignored}
thanks a lot in advance,
Marcel
Quoting from the Google Translate FAQ ...
How do I tell Cloud Translation API to NOT translate something?
You can use the following HTML tags:
<span translate="no"> </span>
<span class="notranslate"> </span>
This functionality requires the source text to be submitted in HTML.
If it's "not working for you" a likely reason is that you requested TEXT translation not HTML translation. If you only want TEXT translation then wrapper your plan text in HTML tags (with <span> tags as above) and then unwrap after translation.
Add a span tag with a class of 'skiptranslate' to the bits that you do not want to be translated, like this:
"This is <span class="skiptranslate">this text will be ignored</span> one continuous sentence."
The "skiptranslate" class didn't work for me in the po files I submitted to gtt. Gtt continued to translate everything. I noticed that gtt does not translate anything inside an href attribute, so what I did was to pre-process my .po file (using a "copy" grunt task) by changing {{my_expr}} type strings to <a href='{{my_expr}}/> strings, then changing them back to {{my_expr}} after the gtt translation (using another "copy" grunt task).
I'm not sure how this affects the semantics of the translation, but at least the resulting translation doesn't break my templating code.
Here is my grunt copy task config showing the regular expressions I used:
copy: {
fixupPoFileForTranslation: {
src: [], // Fill in src and dest!
dest: '',
options : {
process: function (content, srcpath) {
return content.replace(/"Go page"/g, '"Go"')
// Change handlebars {{<name>}} to <a ref='<name>'/> to stop
// machine translation from translating them.
return content.replace(/(\{\{[a-zA-Z_\$].*?\}\})/g, '<a href=\'$1\'/>')
// Same thing for our js .format {0}, {1}, ...
.replace(/(\{\d*?\})/g, '<a href=\'$1\'/>');
}
}
},
fixupPoFileForMerge: {
src: [],
dest: '',
options : {
process: function (content, srcpath) {
// Restore <a href=... back to {{<name>}}
return content..replace(/<a href='(\{\{[a-zA-Z_\$].*?\}\})'(\/>|)/gi, '$1')
// Same thing for {0} constructs
.replace(/<a href=' *(\{.\d?\})('\/>|)/gi, '$1');
}
}
}
}

JIRA layout per-project

Would it be possible to use a project-specific stylesheet for JIRA projects?
For example, if I would like to include project X in an iframe, I'd like to hide the logo and possibly the JIRA toolbar - for specific user groups for example (it's only for viewing purpose, it is not a security feature)
Granted that I'd have to implement this myself (through the webservice api for example) - are there templates for the standard issue page?
Thanks in advance!
There is a (currently undocumented) plugin point in JIRA for inserting top navigation components, <top-navigation>.
You can use this plugin point to add your own navigation bar, and perhaps hide the normal bar using an inline CSS stylesheet. The following example triggers this behavior by using a ?hideit=true query parameter, which is the simplest way to approach the "embed in iframe" problem. You could make that "sticky" by storing it in a session or cookie.
Once you have created a plugin that plugins into the <top-navigation>, hiding the top bar is simple. Here is a velocity script that does it:
#if ($hideHeaderHack)
<style>
\#header {display:none;}
</style>
HIDDEN (remove this message eventually)
#else
NORMAL (remove this message eventually)
#end
To create such a plugin, use the Atlassian Plugin SDK (use atlas-create-jira-plugin). Your atlassian-plugin.xml should look like:
<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
</plugin-info>
<top-navigation key="standard-navigation-top"
name="Tigerblood"
class="com.madbean.topnavhack.TopNav" state='enabled'>
<resource type="velocity" name="view" location="topnav.vm"/>
<order>5</order>
</top-navigation>
</atlassian-plugin>
Your top-navigation implementation class (called com.madbean.topnavhack.TopNav above) should look like:
public class TopNav implements PluggableTopNavigation {
private TopNavigationModuleDescriptor descriptor;
public void init(TopNavigationModuleDescriptor descriptor)
{
this.descriptor = descriptor;
}
public String getHtml(HttpServletRequest request) {
Map<String,Object> params = new HashMap<String, Object>();
params.put("hideHeaderHack", "true".equals(request.getParameter("hideit")));
return descriptor.getTopNavigationHtml(request, params);
}
}
Your plugin will be laid out something like:
./pom.xml
./src/main/java/com/madbean/topnavhack/TopNav.java
./src/main/resources/atlassian-plugin.xml
./src/main/resources/topnav.vm
Disclaimer I work for Atlassian as a developer in the JIRA team.
I don't believe this functionality is exposed directly, and you don't state what JIRA version you are using, but in 4.x in \atlassian-jira\includes\decorators there is a file called bodytop.jsp the has the following fragment that renders the top level navigation and toolbar elements:
// Render all the top nav plugins
for (Iterator iterator = topNavPlugins.iterator(); iterator.hasNext();) {
TopNavigationModuleDescriptor topNavModuleDescriptor = (TopNavigationModuleDescriptor) iterator.next();
PluggableTopNavigation pluggableTopNavigation = (PluggableTopNavigation) topNavModuleDescriptor.getModule();
%>
<%= pluggableTopNavigation.getHtml(request) %>
<%
}
%>
If you wanted to you could create a version of the dashboard rendering jsp that calls a modified bodytop.jsp that renders none of the usual nav elements.
I would be tempted to write a basic plugin to do this.
Take a look at http://confluence.atlassian.com/display/JIRA/Web+Resource+Plugin+Module
If you have yet to write a jira plugin, now might be the time to try it out http://confluence.atlassian.com/display/DEVNET/Developing+your+Plugin+using+the+Atlassian+Plugin+SDK .
I'm currently running Jira 4.2.2 and wrote a plugin that implements PluggableTopNavigation for a custom navigation bar. Unfortunately, this functionality, as detailed in the awarded question, is now depreciated.
My plugin added a div to the top of the Jira header that created a nice menu for use with our development pages. The source of the menu was hard-coded into the plugin and located as a static menu.html file on our server for sharing across different pages.
Since I'd have to completely redesign the plugin for Jira 5.2, I started searching for different ways to re-implement the menu. Here's what I settled on. It's not pretty, but it makes it so you don't have to write a plugin.
Change your announcement banner (quickly get there by typing 'gg', then search for announcement banner) to the following:
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery.get("http://path.to.server/menu.html", function(data){
jQuery("#header").prepend('<nav class="global" role="navigation">'+data+'</nav>');
jQuery("#top-level-id-of-navbar a").css("color", "white")
});
});
</script>
Replace the menu.html link with your own link. The color of the header was inherited by the links in my menu, so I had to change them back to white after inserting the html page.
The result looks identical to Jira 4.2.2, so I'm happy.

Resources